vendor: 12,689 bytes, lines 1-531
1/*! 2 * jQuery JavaScript Library v3.2.1 3 * https://jquery.com/ 4 * 5 * Includes Sizzle.js 6 * https://sizzlejs.com/ 7 * 8 * Copyright JS Foundation and other contributors 9 * Released under the MIT license 10 * https://jquery.org/license 11 * 12 * Date: 2017-03-20T18:59Z 13 */ 14( function( global, factory ) { 15 16 "use strict"; 17 18 if ( typeof module === "object" && typeof module.exports === "object" ) { 19 20 // For CommonJS and CommonJS-like environments where a proper `window` 21 // is present, execute the factory and get jQuery. 22 // For environments that do not have a `window` with a `document` 23 // (such as Node.js), expose a factory as module.exports. 24 // This accentuates the need for the creation of a real `window`. 25 // e.g. var jQuery = require("jquery")(window); 26 // See ticket #14549 for more info. 27 module.exports = global.document ? 28 factory( global, true ) : 29 function( w ) { 30 if ( !w.document ) { 31 throw new Error( "jQuery requires a window with a document" ); 32 } 33 return factory( w ); 34 }; 35 } else { 36 factory( global ); 37 } 38 39// Pass this if window is not defined yet 40} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { 41 42// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 43// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode 44// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common 45// enough that all such attempts are guarded in a try block. 46"use strict"; 47 48var arr = []; 49 50var document = window.document; 51 52var getProto = Object.getPrototypeOf; 53 54var slice = arr.slice; 55 56var concat = arr.concat; 57 58var push = arr.push; 59 60var indexOf = arr.indexOf; 61 62var class2type = {}; 63 64var toString = class2type.toString; 65 66var hasOwn = class2type.hasOwnProperty; 67 68var fnToString = hasOwn.toString; 69 70var ObjectFunctionString = fnToString.call( Object ); 71 72var support = {}; 73 74 75 76 function DOMEval( code, doc ) { 77 doc = doc || document; 78 79 var script = doc.createElement( "script" ); 80 81 script.text = code; 82 doc.head.appendChild( script ).parentNode.removeChild( script ); 83 } 84/* global Symbol */ 85// Defining this global in .eslintrc.json would create a danger of using the global 86// unguarded in another place, it seems safer to define global only for this module 87 88 89 90var 91 version = "3.2.1", 92 93 // Define a local copy of jQuery 94 jQuery = function( selector, context ) { 95 96 // The jQuery object is actually just the init constructor 'enhanced' 97 // Need init if jQuery is called (just allow error to be thrown if not included) 98 return new jQuery.fn.init( selector, context ); 99 }, 100 101 // Support: Android <=4.0 only 102 // Make sure we trim BOM and NBSP 103 rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, 104 105 // Matches dashed string for camelizing 106 rmsPrefix = /^-ms-/, 107 rdashAlpha = /-([a-z])/g, 108 109 // Used by jQuery.camelCase as callback to replace() 110 fcamelCase = function( all, letter ) { 111 return letter.toUpperCase(); 112 }; 113 114jQuery.fn = jQuery.prototype = { 115 116 // The current version of jQuery being used 117 jquery: version, 118 119 constructor: jQuery, 120 121 // The default length of a jQuery object is 0 122 length: 0, 123 124 toArray: function() { 125 return slice.call( this ); 126 }, 127 128 // Get the Nth element in the matched element set OR 129 // Get the whole matched element set as a clean array 130 get: function( num ) { 131 132 // Return all the elements in a clean array 133 if ( num == null ) { 134 return slice.call( this ); 135 } 136 137 // Return just the one element from the set 138 return num < 0 ? this[ num + this.length ] : this[ num ]; 139 }, 140 141 // Take an array of elements and push it onto the stack 142 // (returning the new matched element set) 143 pushStack: function( elems ) { 144 145 // Build a new jQuery matched element set 146 var ret = jQuery.merge( this.constructor(), elems ); 147 148 // Add the old object onto the stack (as a reference) 149 ret.prevObject = this; 150 151 // Return the newly-formed element set 152 return ret; 153 }, 154 155 // Execute a callback for every element in the matched set. 156 each: function( callback ) { 157 return jQuery.each( this, callback ); 158 }, 159 160 map: function( callback ) { 161 return this.pushStack( jQuery.map( this, function( elem, i ) { 162 return callback.call( elem, i, elem ); 163 } ) ); 164 }, 165 166 slice: function() { 167 return this.pushStack( slice.apply( this, arguments ) ); 168 }, 169 170 first: function() { 171 return this.eq( 0 ); 172 }, 173 174 last: function() { 175 return this.eq( -1 ); 176 }, 177 178 eq: function( i ) { 179 var len = this.length, 180 j = +i + ( i < 0 ? len : 0 ); 181 return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); 182 }, 183 184 end: function() { 185 return this.prevObject || this.constructor(); 186 }, 187 188 // For internal use only. 189 // Behaves like an Array's method, not like a jQuery method. 190 push: push, 191 sort: arr.sort, 192 splice: arr.splice 193}; 194 195jQuery.extend = jQuery.fn.extend = function() { 196 var options, name, src, copy, copyIsArray, clone, 197 target = arguments[ 0 ] || {}, 198 i = 1, 199 length = arguments.length, 200 deep = false; 201 202 // Handle a deep copy situation 203 if ( typeof target === "boolean" ) { 204 deep = target; 205 206 // Skip the boolean and the target 207 target = arguments[ i ] || {}; 208 i++; 209 } 210 211 // Handle case when target is a string or something (possible in deep copy) 212 if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { 213 target = {}; 214 } 215 216 // Extend jQuery itself if only one argument is passed 217 if ( i === length ) { 218 target = this; 219 i--; 220 } 221 222 for ( ; i < length; i++ ) { 223 224 // Only deal with non-null/undefined values 225 if ( ( options = arguments[ i ] ) != null ) { 226 227 // Extend the base object 228 for ( name in options ) { 229 src = target[ name ]; 230 copy = options[ name ]; 231 232 // Prevent never-ending loop 233 if ( target === copy ) { 234 continue; 235 } 236 237 // Recurse if we're merging plain objects or arrays 238 if ( deep && copy && ( jQuery.isPlainObject( copy ) || 239 ( copyIsArray = Array.isArray( copy ) ) ) ) { 240 241 if ( copyIsArray ) { 242 copyIsArray = false; 243 clone = src && Array.isArray( src ) ? src : []; 244 245 } else { 246 clone = src && jQuery.isPlainObject( src ) ? src : {}; 247 } 248 249 // Never move original objects, clone them 250 target[ name ] = jQuery.extend( deep, clone, copy ); 251 252 // Don't bring in undefined values 253 } else if ( copy !== undefined ) { 254 target[ name ] = copy; 255 } 256 } 257 } 258 } 259 260 // Return the modified object 261 return target; 262}; 263 264jQuery.extend( { 265 266 // Unique for each copy of jQuery on the page 267 expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), 268 269 // Assume jQuery is ready without the ready module 270 isReady: true, 271 272 error: function( msg ) { 273 throw new Error( msg ); 274 }, 275 276 noop: function() {}, 277 278 isFunction: function( obj ) { 279 return jQuery.type( obj ) === "function"; 280 }, 281 282 isWindow: function( obj ) { 283 return obj != null && obj === obj.window; 284 }, 285 286 isNumeric: function( obj ) { 287 288 // As of jQuery 3.0, isNumeric is limited to 289 // strings and numbers (primitives or objects) 290 // that can be coerced to finite numbers (gh-2662) 291 var type = jQuery.type( obj ); 292 return ( type === "number" || type === "string" ) && 293 294 // parseFloat NaNs numeric-cast false positives ("") 295 // ...but misinterprets leading-number strings, particularly hex literals ("0x...") 296 // subtraction forces infinities to NaN 297 !isNaN( obj - parseFloat( obj ) ); 298 }, 299 300 isPlainObject: function( obj ) { 301 var proto, Ctor; 302 303 // Detect obvious negatives 304 // Use toString instead of jQuery.type to catch host objects 305 if ( !obj || toString.call( obj ) !== "[object Object]" ) { 306 return false; 307 } 308 309 proto = getProto( obj ); 310 311 // Objects with no prototype (e.g., `Object.create( null )`) are plain 312 if ( !proto ) { 313 return true; 314 } 315 316 // Objects with prototype are plain iff they were constructed by a global Object function 317 Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; 318 return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; 319 }, 320 321 isEmptyObject: function( obj ) { 322 323 /* eslint-disable no-unused-vars */ 324 // See https://github.com/eslint/eslint/issues/6125 325 var name; 326 327 for ( name in obj ) { 328 return false; 329 } 330 return true; 331 }, 332 333 type: function( obj ) { 334 if ( obj == null ) { 335 return obj + ""; 336 } 337 338 // Support: Android <=2.3 only (functionish RegExp) 339 return typeof obj === "object" || typeof obj === "function" ? 340 class2type[ toString.call( obj ) ] || "object" : 341 typeof obj; 342 }, 343 344 // Evaluates a script in a global context 345 globalEval: function( code ) { 346 DOMEval( code ); 347 }, 348 349 // Convert dashed to camelCase; used by the css and data modules 350 // Support: IE <=9 - 11, Edge 12 - 13 351 // Microsoft forgot to hump their vendor prefix (#9572) 352 camelCase: function( string ) { 353 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); 354 }, 355 356 each: function( obj, callback ) { 357 var length, i = 0; 358 359 if ( isArrayLike( obj ) ) { 360 length = obj.length; 361 for ( ; i < length; i++ ) { 362 if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { 363 break; 364 } 365 } 366 } else { 367 for ( i in obj ) { 368 if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { 369 break; 370 } 371 } 372 } 373 374 return obj; 375 }, 376 377 // Support: Android <=4.0 only 378 trim: function( text ) { 379 return text == null ? 380 "" : 381 ( text + "" ).replace( rtrim, "" ); 382 }, 383 384 // results is for internal usage only 385 makeArray: function( arr, results ) { 386 var ret = results || []; 387 388 if ( arr != null ) { 389 if ( isArrayLike( Object( arr ) ) ) { 390 jQuery.merge( ret, 391 typeof arr === "string" ? 392 [ arr ] : arr 393 ); 394 } else { 395 push.call( ret, arr ); 396 } 397 } 398 399 return ret; 400 }, 401 402 inArray: function( elem, arr, i ) { 403 return arr == null ? -1 : indexOf.call( arr, elem, i ); 404 }, 405 406 // Support: Android <=4.0 only, PhantomJS 1 only 407 // push.apply(_, arraylike) throws on ancient WebKit 408 merge: function( first, second ) { 409 var len = +second.length, 410 j = 0, 411 i = first.length; 412 413 for ( ; j < len; j++ ) { 414 first[ i++ ] = second[ j ]; 415 } 416 417 first.length = i; 418 419 return first; 420 }, 421 422 grep: function( elems, callback, invert ) { 423 var callbackInverse, 424 matches = [], 425 i = 0, 426 length = elems.length, 427 callbackExpect = !invert; 428 429 // Go through the array, only saving the items 430 // that pass the validator function 431 for ( ; i < length; i++ ) { 432 callbackInverse = !callback( elems[ i ], i ); 433 if ( callbackInverse !== callbackExpect ) { 434 matches.push( elems[ i ] ); 435 } 436 } 437 438 return matches; 439 }, 440 441 // arg is for internal usage only 442 map: function( elems, callback, arg ) { 443 var length, value, 444 i = 0, 445 ret = []; 446 447 // Go through the array, translating each of the items to their new values 448 if ( isArrayLike( elems ) ) { 449 length = elems.length; 450 for ( ; i < length; i++ ) { 451 value = callback( elems[ i ], i, arg ); 452 453 if ( value != null ) { 454 ret.push( value ); 455 } 456 } 457 458 // Go through every key on the object, 459 } else { 460 for ( i in elems ) { 461 value = callback( elems[ i ], i, arg ); 462 463 if ( value != null ) { 464 ret.push( value ); 465 } 466 } 467 } 468 469 // Flatten any nested arrays 470 return concat.apply( [], ret ); 471 }, 472 473 // A global GUID counter for objects 474 guid: 1, 475 476 // Bind a function to a context, optionally partially applying any 477 // arguments. 478 proxy: function( fn, context ) { 479 var tmp, args, proxy; 480 481 if ( typeof context === "string" ) { 482 tmp = fn[ context ]; 483 context = fn; 484 fn = tmp; 485 } 486 487 // Quick check to determine if target is callable, in the spec 488 // this throws a TypeError, but we will just return undefined. 489 if ( !jQuery.isFunction( fn ) ) { 490 return undefined; 491 } 492 493 // Simulated bind 494 args = slice.call( arguments, 2 ); 495 proxy = function() { 496 return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); 497 }; 498 499 // Set the guid of unique handler to the same of original handler, so it can be removed 500 proxy.guid = fn.guid = fn.guid || jQuery.guid++; 501 502 return proxy; 503 }, 504 505 now: Date.now, 506 507 // jQuery.support is not used in Core but other projects attach their 508 // properties to it so it needs to exist. 509 support: support 510} ); 511 512if ( typeof Symbol === "function" ) { 513 jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; 514} 515 516// Populate the class2type map 517jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), 518function( i, name ) { 519 class2type[ "[object " + name + "]" ] = name.toLowerCase(); 520} ); 521 522function isArrayLike( obj ) { 523 524 // Support: real iOS 8.2 only (not reproducible in simulator) 525 // `in` check used to prevent JIT error (gh-2145) 526 // hasOwn isn't used here due to false negatives 527 // regarding Nodelist length in IE 528 var length = !!obj && "length" in obj && obj.length, 529 type = jQuery.type( obj ); 530 531 if ( type === "function" || jQuery.
vendor: 31,442 bytes, lines 531-1628
531isWindow( obj ) ) { 532 return false; 533 } 534 535 return type === "array" || length === 0 || 536 typeof length === "number" && length > 0 && ( length - 1 ) in obj; 537} 538var Sizzle = 539/*! 540 * Sizzle CSS Selector Engine v2.3.3 541 * https://sizzlejs.com/ 542 * 543 * Copyright jQuery Foundation and other contributors 544 * Released under the MIT license 545 * http://jquery.org/license 546 * 547 * Date: 2016-08-08 548 */ 549(function( window ) { 550 551var i, 552 support, 553 Expr, 554 getText, 555 isXML, 556 tokenize, 557 compile, 558 select, 559 outermostContext, 560 sortInput, 561 hasDuplicate, 562 563 // Local document vars 564 setDocument, 565 document, 566 docElem, 567 documentIsHTML, 568 rbuggyQSA, 569 rbuggyMatches, 570 matches, 571 contains, 572 573 // Instance-specific data 574 expando = "sizzle" + 1 * new Date(), 575 preferredDoc = window.document, 576 dirruns = 0, 577 done = 0, 578 classCache = createCache(), 579 tokenCache = createCache(), 580 compilerCache = createCache(), 581 sortOrder = function( a, b ) { 582 if ( a === b ) { 583 hasDuplicate = true; 584 } 585 return 0; 586 }, 587 588 // Instance methods 589 hasOwn = ({}).hasOwnProperty, 590 arr = [], 591 pop = arr.pop, 592 push_native = arr.push, 593 push = arr.push, 594 slice = arr.slice, 595 // Use a stripped-down indexOf as it's faster than native 596 // https://jsperf.com/thor-indexof-vs-for/5 597 indexOf = function( list, elem ) { 598 var i = 0, 599 len = list.length; 600 for ( ; i < len; i++ ) { 601 if ( list[i] === elem ) { 602 return i; 603 } 604 } 605 return -1; 606 }, 607 608 booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", 609 610 // Regular expressions 611 612 // http://www.w3.org/TR/css3-selectors/#whitespace 613 whitespace = "[\\x20\\t\\r\\n\\f]", 614 615 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier 616 identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", 617 618 // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors 619 attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + 620 // Operator (capture 2) 621 "*([*^$|!~]?=)" + whitespace + 622 // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" 623 "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + 624 "*\\]", 625 626 pseudos = ":(" + identifier + ")(?:\\((" + 627 // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: 628 // 1. quoted (capture 3; capture 4 or capture 5) 629 "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + 630 // 2. simple (capture 6) 631 "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + 632 // 3. anything else (capture 2) 633 ".*" + 634 ")\\)|)", 635 636 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter 637 rwhitespace = new RegExp( whitespace + "+", "g" ), 638 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), 639 640 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), 641 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), 642 643 rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), 644 645 rpseudo = new RegExp( pseudos ), 646 ridentifier = new RegExp( "^" + identifier + "$" ), 647 648 matchExpr = { 649 "ID": new RegExp( "^#(" + identifier + ")" ), 650 "CLASS": new RegExp( "^\\.(" + identifier + ")" ), 651 "TAG": new RegExp( "^(" + identifier + "|[*])" ), 652 "ATTR": new RegExp( "^" + attributes ), 653 "PSEUDO": new RegExp( "^" + pseudos ), 654 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + 655 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + 656 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), 657 "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), 658 // For use in libraries implementing .is() 659 // We use this for POS matching in `select` 660 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + 661 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) 662 }, 663 664 rinputs = /^(?:input|select|textarea|button)$/i, 665 rheader = /^h\d$/i, 666 667 rnative = /^[^{]+\{\s*\[native \w/, 668 669 // Easily-parseable/retrievable ID or TAG or CLASS selectors 670 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, 671 672 rsibling = /[+~]/, 673 674 // CSS escapes 675 // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters 676 runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), 677 funescape = function( _, escaped, escapedWhitespace ) { 678 var high = "0x" + escaped - 0x10000; 679 // NaN means non-codepoint 680 // Support: Firefox<24 681 // Workaround erroneous numeric interpretation of +"0x" 682 return high !== high || escapedWhitespace ? 683 escaped : 684 high < 0 ? 685 // BMP codepoint 686 String.fromCharCode( high + 0x10000 ) : 687 // Supplemental Plane codepoint (surrogate pair) 688 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); 689 }, 690 691 // CSS string/identifier serialization 692 // https://drafts.csswg.org/cssom/#common-serializing-idioms 693 rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, 694 fcssescape = function( ch, asCodePoint ) { 695 if ( asCodePoint ) { 696 697 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 698 if ( ch === "\0" ) { 699 return "\uFFFD"; 700 } 701 702 // Control characters and (dependent upon position) numbers get escaped as code points 703 return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; 704 } 705 706 // Other potentially-special ASCII characters get backslash-escaped 707 return "\\" + ch; 708 }, 709 710 // Used for iframes 711 // See setDocument() 712 // Removing the function wrapper causes a "Permission Denied" 713 // error in IE 714 unloadHandler = function() { 715 setDocument(); 716 }, 717 718 disabledAncestor = addCombinator( 719 function( elem ) { 720 return elem.disabled === true && ("form" in elem || "label" in elem); 721 }, 722 { dir: "parentNode", next: "legend" } 723 ); 724 725// Optimize for push.apply( _, NodeList ) 726try { 727 push.apply( 728 (arr = slice.call( preferredDoc.childNodes )), 729 preferredDoc.childNodes 730 ); 731 // Support: Android<4.0 732 // Detect silently failing push.apply 733 arr[ preferredDoc.childNodes.length ].nodeType; 734} catch ( e ) { 735 push = { apply: arr.length ? 736 737 // Leverage slice if possible 738 function( target, els ) { 739 push_native.apply( target, slice.call(els) ); 740 } : 741 742 // Support: IE<9 743 // Otherwise append directly 744 function( target, els ) { 745 var j = target.length, 746 i = 0; 747 // Can't trust NodeList.length 748 while ( (target[j++] = els[i++]) ) {} 749 target.length = j - 1; 750 } 751 }; 752} 753 754function Sizzle( selector, context, results, seed ) { 755 var m, i, elem, nid, match, groups, newSelector, 756 newContext = context && context.ownerDocument, 757 758 // nodeType defaults to 9, since context defaults to document 759 nodeType = context ? context.nodeType : 9; 760 761 results = results || []; 762 763 // Return early from calls with invalid selector or context 764 if ( typeof selector !== "string" || !selector || 765 nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { 766 767 return results; 768 } 769 770 // Try to shortcut find operations (as opposed to filters) in HTML documents 771 if ( !seed ) { 772 773 if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { 774 setDocument( context ); 775 } 776 context = context || document; 777 778 if ( documentIsHTML ) { 779 780 // If the selector is sufficiently simple, try using a "get*By*" DOM method 781 // (excepting DocumentFragment context, where the methods don't exist) 782 if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { 783 784 // ID selector 785 if ( (m = match[1]) ) { 786 787 // Document context 788 if ( nodeType === 9 ) { 789 if ( (elem = context.getElementById( m )) ) { 790 791 // Support: IE, Opera, Webkit 792 // TODO: identify versions 793 // getElementById can match elements by name instead of ID 794 if ( elem.id === m ) { 795 results.push( elem ); 796 return results; 797 } 798 } else { 799 return results; 800 } 801 802 // Element context 803 } else { 804 805 // Support: IE, Opera, Webkit 806 // TODO: identify versions 807 // getElementById can match elements by name instead of ID 808 if ( newContext && (elem = newContext.getElementById( m )) && 809 contains( context, elem ) && 810 elem.id === m ) { 811 812 results.push( elem ); 813 return results; 814 } 815 } 816 817 // Type selector 818 } else if ( match[2] ) { 819 push.apply( results, context.getElementsByTagName( selector ) ); 820 return results; 821 822 // Class selector 823 } else if ( (m = match[3]) && support.getElementsByClassName && 824 context.getElementsByClassName ) { 825 826 push.apply( results, context.getElementsByClassName( m ) ); 827 return results; 828 } 829 } 830 831 // Take advantage of querySelectorAll 832 if ( support.qsa && 833 !compilerCache[ selector + " " ] && 834 (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { 835 836 if ( nodeType !== 1 ) { 837 newContext = context; 838 newSelector = selector; 839 840 // qSA looks outside Element context, which is not what we want 841 // Thanks to Andrew Dupont for this workaround technique 842 // Support: IE <=8 843 // Exclude object elements 844 } else if ( context.nodeName.toLowerCase() !== "object" ) { 845 846 // Capture the context ID, setting it first if necessary 847 if ( (nid = context.getAttribute( "id" )) ) { 848 nid = nid.replace( rcssescape, fcssescape ); 849 } else { 850 context.setAttribute( "id", (nid = expando) ); 851 } 852 853 // Prefix every selector in the list 854 groups = tokenize( selector ); 855 i = groups.length; 856 while ( i-- ) { 857 groups[i] = "#" + nid + " " + toSelector( groups[i] ); 858 } 859 newSelector = groups.join( "," ); 860 861 // Expand context for sibling selectors 862 newContext = rsibling.test( selector ) && testContext( context.parentNode ) || 863 context; 864 } 865 866 if ( newSelector ) { 867 try { 868 push.apply( results, 869 newContext.querySelectorAll( newSelector ) 870 ); 871 return results; 872 } catch ( qsaError ) { 873 } finally { 874 if ( nid === expando ) { 875 context.removeAttribute( "id" ); 876 } 877 } 878 } 879 } 880 } 881 } 882 883 // All others 884 return select( selector.replace( rtrim, "$1" ), context, results, seed ); 885} 886 887/** 888 * Create key-value caches of limited size 889 * @returns {function(string, object)} Returns the Object data after storing it on itself with 890 * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) 891 * deleting the oldest entry 892 */ 893function createCache() { 894 var keys = []; 895 896 function cache( key, value ) { 897 // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) 898 if ( keys.push( key + " " ) > Expr.cacheLength ) { 899 // Only keep the most recent entries 900 delete cache[ keys.shift() ]; 901 } 902 return (cache[ key + " " ] = value); 903 } 904 return cache; 905} 906 907/** 908 * Mark a function for special use by Sizzle 909 * @param {Function} fn The function to mark 910 */ 911function markFunction( fn ) { 912 fn[ expando ] = true; 913 return fn; 914} 915 916/** 917 * Support testing using an element 918 * @param {Function} fn Passed the created element and returns a boolean result 919 */ 920function assert( fn ) { 921 var el = document.createElement("fieldset"); 922 923 try { 924 return !!fn( el ); 925 } catch (e) { 926 return false; 927 } finally { 928 // Remove from its parent by default 929 if ( el.parentNode ) { 930 el.parentNode.removeChild( el ); 931 } 932 // release memory in IE 933 el = null; 934 } 935} 936 937/** 938 * Adds the same handler for all of the specified attrs 939 * @param {String} attrs Pipe-separated list of attributes 940 * @param {Function} handler The method that will be applied 941 */ 942function addHandle( attrs, handler ) { 943 var arr = attrs.split("|"), 944 i = arr.length; 945 946 while ( i-- ) { 947 Expr.attrHandle[ arr[i] ] = handler; 948 } 949} 950 951/** 952 * Checks document order of two siblings 953 * @param {Element} a 954 * @param {Element} b 955 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b 956 */ 957function siblingCheck( a, b ) { 958 var cur = b && a, 959 diff = cur && a.nodeType === 1 && b.nodeType === 1 && 960 a.sourceIndex - b.sourceIndex; 961 962 // Use IE sourceIndex if available on both nodes 963 if ( diff ) { 964 return diff; 965 } 966 967 // Check if b follows a 968 if ( cur ) { 969 while ( (cur = cur.nextSibling) ) { 970 if ( cur === b ) { 971 return -1; 972 } 973 } 974 } 975 976 return a ? 1 : -1; 977} 978 979/** 980 * Returns a function to use in pseudos for input types 981 * @param {String} type 982 */ 983function createInputPseudo( type ) { 984 return function( elem ) { 985 var name = elem.nodeName.toLowerCase(); 986 return name === "input" && elem.type === type; 987 }; 988} 989 990/** 991 * Returns a function to use in pseudos for buttons 992 * @param {String} type 993 */ 994function createButtonPseudo( type ) { 995 return function( elem ) { 996 var name = elem.nodeName.toLowerCase(); 997 return (name === "input" || name === "button") && elem.type === type; 998 }; 999} 1000 1001/** 1002 * Returns a function to use in pseudos for :enabled/:disabled 1003 * @param {Boolean} disabled true for :disabled; false for :enabled 1004 */ 1005function createDisabledPseudo( disabled ) { 1006 1007 // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable 1008 return function( elem ) { 1009 1010 // Only certain elements can match :enabled or :disabled 1011 // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled 1012 // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled 1013 if ( "form" in elem ) { 1014 1015 // Check for inherited disabledness on relevant non-disabled elements: 1016 // * listed form-associated elements in a disabled fieldset 1017 // https://html.spec.whatwg.org/multipage/forms.html#category-listed 1018 // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled 1019 // * option elements in a disabled optgroup 1020 // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled 1021 // All such elements have a "form" property. 1022 if ( elem.parentNode && elem.disabled === false ) { 1023 1024 // Option elements defer to a parent optgroup if present 1025 if ( "label" in elem ) { 1026 if ( "label" in elem.parentNode ) { 1027 return elem.parentNode.disabled === disabled; 1028 } else { 1029 return elem.disabled === disabled; 1030 } 1031 } 1032 1033 // Support: IE 6 - 11 1034 // Use the isDisabled shortcut property to check for disabled fieldset ancestors 1035 return elem.isDisabled === disabled || 1036 1037 // Where there is no isDisabled, check manually 1038 /* jshint -W018 */ 1039 elem.isDisabled !== !disabled && 1040 disabledAncestor( elem ) === disabled; 1041 } 1042 1043 return elem.disabled === disabled; 1044 1045 // Try to winnow out elements that can't be disabled before trusting the disabled property. 1046 // Some victims get caught in our net (label, legend, menu, track), but it shouldn't 1047 // even exist on them, let alone have a boolean value. 1048 } else if ( "label" in elem ) { 1049 return elem.disabled === disabled; 1050 } 1051 1052 // Remaining elements are neither :enabled nor :disabled 1053 return false; 1054 }; 1055} 1056 1057/** 1058 * Returns a function to use in pseudos for positionals 1059 * @param {Function} fn 1060 */ 1061function createPositionalPseudo( fn ) { 1062 return markFunction(function( argument ) { 1063 argument = +argument; 1064 return markFunction(function( seed, matches ) { 1065 var j, 1066 matchIndexes = fn( [], seed.length, argument ), 1067 i = matchIndexes.length; 1068 1069 // Match elements found at the specified indexes 1070 while ( i-- ) { 1071 if ( seed[ (j = matchIndexes[i]) ] ) { 1072 seed[j] = !(matches[j] = seed[j]); 1073 } 1074 } 1075 }); 1076 }); 1077} 1078 1079/** 1080 * Checks a node for validity as a Sizzle context 1081 * @param {Element|Object=} context 1082 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value 1083 */ 1084function testContext( context ) { 1085 return context && typeof context.getElementsByTagName !== "undefined" && context; 1086} 1087 1088// Expose support vars for convenience 1089support = Sizzle.support = {}; 1090 1091/** 1092 * Detects XML nodes 1093 * @param {Element|Object} elem An element or a document 1094 * @returns {Boolean} True iff elem is a non-HTML XML node 1095 */ 1096isXML = Sizzle.isXML = function( elem ) { 1097 // documentElement is verified for cases where it doesn't yet exist 1098 // (such as loading iframes in IE - #4833) 1099 var documentElement = elem && (elem.ownerDocument || elem).documentElement; 1100 return documentElement ? documentElement.nodeName !== "HTML" : false; 1101}; 1102 1103/** 1104 * Sets document-related variables once based on the current document 1105 * @param {Element|Object} [doc] An element or document object to use to set the document 1106 * @returns {Object} Returns the current document 1107 */ 1108setDocument = Sizzle.setDocument = function( node ) { 1109 var hasCompare, subWindow, 1110 doc = node ? node.ownerDocument || node : preferredDoc; 1111 1112 // Return early if doc is invalid or already selected 1113 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { 1114 return document; 1115 } 1116 1117 // Update global variables 1118 document = doc; 1119 docElem = document.documentElement; 1120 documentIsHTML = !isXML( document ); 1121 1122 // Support: IE 9-11, Edge 1123 // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) 1124 if ( preferredDoc !== document && 1125 (subWindow = document.defaultView) && subWindow.top !== subWindow ) { 1126 1127 // Support: IE 11, Edge 1128 if ( subWindow.addEventListener ) { 1129 subWindow.addEventListener( "unload", unloadHandler, false ); 1130 1131 // Support: IE 9 - 10 only 1132 } else if ( subWindow.attachEvent ) { 1133 subWindow.attachEvent( "onunload", unloadHandler ); 1134 } 1135 } 1136 1137 /* Attributes 1138 ---------------------------------------------------------------------- */ 1139 1140 // Support: IE<8 1141 // Verify that getAttribute really returns attributes and not properties 1142 // (excepting IE8 booleans) 1143 support.attributes = assert(function( el ) { 1144 el.className = "i"; 1145 return !el.getAttribute("className"); 1146 }); 1147 1148 /* getElement(s)By* 1149 ---------------------------------------------------------------------- */ 1150 1151 // Check if getElementsByTagName("*") returns only elements 1152 support.getElementsByTagName = assert(function( el ) { 1153 el.appendChild( document.createComment("") ); 1154 return !el.getElementsByTagName("*").length; 1155 }); 1156 1157 // Support: IE<9 1158 support.getElementsByClassName = rnative.test( document.getElementsByClassName ); 1159 1160 // Support: IE<10 1161 // Check if getElementById returns elements by name 1162 // The broken getElementById methods don't pick up programmatically-set names, 1163 // so use a roundabout getElementsByName test 1164 support.getById = assert(function( el ) { 1165 docElem.appendChild( el ).id = expando; 1166 return !document.getElementsByName || !document.getElementsByName( expando ).length; 1167 }); 1168 1169 // ID filter and find 1170 if ( support.getById ) { 1171 Expr.filter["ID"] = function( id ) { 1172 var attrId = id.replace( runescape, funescape ); 1173 return function( elem ) { 1174 return elem.getAttribute("id") === attrId; 1175 }; 1176 }; 1177 Expr.find["ID"] = function( id, context ) { 1178 if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { 1179 var elem = context.getElementById( id ); 1180 return elem ? [ elem ] : []; 1181 } 1182 }; 1183 } else { 1184 Expr.filter["ID"] = function( id ) { 1185 var attrId = id.replace( runescape, funescape ); 1186 return function( elem ) { 1187 var node = typeof elem.getAttributeNode !== "undefined" && 1188 elem.getAttributeNode("id"); 1189 return node && node.value === attrId; 1190 }; 1191 }; 1192 1193 // Support: IE 6 - 7 only 1194 // getElementById is not reliable as a find shortcut 1195 Expr.find["ID"] = function( id, context ) { 1196 if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { 1197 var node, i, elems, 1198 elem = context.getElementById( id ); 1199 1200 if ( elem ) { 1201 1202 // Verify the id attribute 1203 node = elem.getAttributeNode("id"); 1204 if ( node && node.value === id ) { 1205 return [ elem ]; 1206 } 1207 1208 // Fall back on getElementsByName 1209 elems = context.getElementsByName( id ); 1210 i = 0; 1211 while ( (elem = elems[i++]) ) { 1212 node = elem.getAttributeNode("id"); 1213 if ( node && node.value === id ) { 1214 return [ elem ]; 1215 } 1216 } 1217 } 1218 1219 return []; 1220 } 1221 }; 1222 } 1223 1224 // Tag 1225 Expr.find["TAG"] = support.getElementsByTagName ? 1226 function( tag, context ) { 1227 if ( typeof context.getElementsByTagName !== "undefined" ) { 1228 return context.getElementsByTagName( tag ); 1229 1230 // DocumentFragment nodes don't have gEBTN 1231 } else if ( support.qsa ) { 1232 return context.querySelectorAll( tag ); 1233 } 1234 } : 1235 1236 function( tag, context ) { 1237 var elem, 1238 tmp = [], 1239 i = 0, 1240 // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too 1241 results = context.getElementsByTagName( tag ); 1242 1243 // Filter out possible comments 1244 if ( tag === "*" ) { 1245 while ( (elem = results[i++]) ) { 1246 if ( elem.nodeType === 1 ) { 1247 tmp.push( elem ); 1248 } 1249 } 1250 1251 return tmp; 1252 } 1253 return results; 1254 }; 1255 1256 // Class 1257 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { 1258 if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { 1259 return context.getElementsByClassName( className ); 1260 } 1261 }; 1262 1263 /* QSA/matchesSelector 1264 ---------------------------------------------------------------------- */ 1265 1266 // QSA and matchesSelector support 1267 1268 // matchesSelector(:active) reports false when true (IE9/Opera 11.5) 1269 rbuggyMatches = []; 1270 1271 // qSa(:focus) reports false when true (Chrome 21) 1272 // We allow this because of a bug in IE8/9 that throws an error 1273 // whenever `document.activeElement` is accessed on an iframe 1274 // So, we allow :focus to pass through QSA all the time to avoid the IE error 1275 // See https://bugs.jquery.com/ticket/13378 1276 rbuggyQSA = []; 1277 1278 if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { 1279 // Build QSA regex 1280 // Regex strategy adopted from Diego Perini 1281 assert(function( el ) { 1282 // Select is set to empty string on purpose 1283 // This is to test IE's treatment of not explicitly 1284 // setting a boolean content attribute, 1285 // since its presence should be enough 1286 // https://bugs.jquery.com/ticket/12359 1287 docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" + 1288 "<select id='" + expando + "-\r\\' msallowcapture=''>" + 1289 "<option selected=''></option></select>"; 1290 1291 // Support: IE8, Opera 11-12.16 1292 // Nothing should be selected when empty strings follow ^= or $= or *= 1293 // The test attribute must be unknown in Opera but "safe" for WinRT 1294 // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section 1295 if ( el.querySelectorAll("[msallowcapture^='']").length ) { 1296 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); 1297 } 1298 1299 // Support: IE8 1300 // Boolean attributes and "value" are not treated correctly 1301 if ( !el.querySelectorAll("[selected]").length ) { 1302 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); 1303 } 1304 1305 // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ 1306 if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { 1307 rbuggyQSA.push("~="); 1308 } 1309 1310 // Webkit/Opera - :checked should return selected option elements 1311 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked 1312 // IE8 throws error here and will not see later tests 1313 if ( !el.querySelectorAll(":checked").length ) { 1314 rbuggyQSA.push(":checked"); 1315 } 1316 1317 // Support: Safari 8+, iOS 8+ 1318 // https://bugs.webkit.org/show_bug.cgi?id=136851 1319 // In-page `selector#id sibling-combinator selector` fails 1320 if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { 1321 rbuggyQSA.push(".#.+[+~]"); 1322 } 1323 }); 1324 1325 assert(function( el ) { 1326 el.innerHTML = "<a href='' disabled='disabled'></a>" + 1327 "<select disabled='disabled'><option/></select>"; 1328 1329 // Support: Windows 8 Native Apps 1330 // The type and name attributes are restricted during .innerHTML assignment 1331 var input = document.createElement("input"); 1332 input.setAttribute( "type", "hidden" ); 1333 el.appendChild( input ).setAttribute( "name", "D" ); 1334 1335 // Support: IE8 1336 // Enforce case-sensitivity of name attribute 1337 if ( el.querySelectorAll("[name=d]").length ) { 1338 rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); 1339 } 1340 1341 // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) 1342 // IE8 throws error here and will not see later tests 1343 if ( el.querySelectorAll(":enabled").length !== 2 ) { 1344 rbuggyQSA.push( ":enabled", ":disabled" ); 1345 } 1346 1347 // Support: IE9-11+ 1348 // IE's :disabled selector does not pick up the children of disabled fieldsets 1349 docElem.appendChild( el ).disabled = true; 1350 if ( el.querySelectorAll(":disabled").length !== 2 ) { 1351 rbuggyQSA.push( ":enabled", ":disabled" ); 1352 } 1353 1354 // Opera 10-11 does not throw on post-comma invalid pseudos 1355 el.querySelectorAll("*,:x"); 1356 rbuggyQSA.push(",.*:"); 1357 }); 1358 } 1359 1360 if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || 1361 docElem.webkitMatchesSelector || 1362 docElem.mozMatchesSelector || 1363 docElem.oMatchesSelector || 1364 docElem.msMatchesSelector) )) ) { 1365 1366 assert(function( el ) { 1367 // Check to see if it's possible to do matchesSelector 1368 // on a disconnected node (IE 9) 1369 support.disconnectedMatch = matches.call( el, "*" ); 1370 1371 // This should fail with an exception 1372 // Gecko does not error, returns false instead 1373 matches.call( el, "[s!='']:x" ); 1374 rbuggyMatches.push( "!=", pseudos ); 1375 }); 1376 } 1377 1378 rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); 1379 rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); 1380 1381 /* Contains 1382 ---------------------------------------------------------------------- */ 1383 hasCompare = rnative.test( docElem.compareDocumentPosition ); 1384 1385 // Element contains another 1386 // Purposefully self-exclusive 1387 // As in, an element does not contain itself 1388 contains = hasCompare || rnative.test( docElem.contains ) ? 1389 function( a, b ) { 1390 var adown = a.nodeType === 9 ? a.documentElement : a, 1391 bup = b && b.parentNode; 1392 return a === bup || !!( bup && bup.nodeType === 1 && ( 1393 adown.contains ? 1394 adown.contains( bup ) : 1395 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 1396 )); 1397 } : 1398 function( a, b ) { 1399 if ( b ) { 1400 while ( (b = b.parentNode) ) { 1401 if ( b === a ) { 1402 return true; 1403 } 1404 } 1405 } 1406 return false; 1407 }; 1408 1409 /* Sorting 1410 ---------------------------------------------------------------------- */ 1411 1412 // Document order sorting 1413 sortOrder = hasCompare ? 1414 function( a, b ) { 1415 1416 // Flag for duplicate removal 1417 if ( a === b ) { 1418 hasDuplicate = true; 1419 return 0; 1420 } 1421 1422 // Sort on method existence if only one input has compareDocumentPosition 1423 var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; 1424 if ( compare ) { 1425 return compare; 1426 } 1427 1428 // Calculate position if both inputs belong to the same document 1429 compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? 1430 a.compareDocumentPosition( b ) : 1431 1432 // Otherwise we know they are disconnected 1433 1; 1434 1435 // Disconnected nodes 1436 if ( compare & 1 || 1437 (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { 1438 1439 // Choose the first element that is related to our preferred document 1440 if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { 1441 return -1; 1442 } 1443 if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { 1444 return 1; 1445 } 1446 1447 // Maintain original order 1448 return sortInput ? 1449 ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 1450 0; 1451 } 1452 1453 return compare & 4 ? -1 : 1; 1454 } : 1455 function( a, b ) { 1456 // Exit early if the nodes are identical 1457 if ( a === b ) { 1458 hasDuplicate = true; 1459 return 0; 1460 } 1461 1462 var cur, 1463 i = 0, 1464 aup = a.parentNode, 1465 bup = b.parentNode, 1466 ap = [ a ], 1467 bp = [ b ]; 1468 1469 // Parentless nodes are either documents or disconnected 1470 if ( !aup || !bup ) { 1471 return a === document ? -1 : 1472 b === document ? 1 : 1473 aup ? -1 : 1474 bup ? 1 : 1475 sortInput ? 1476 ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 1477 0; 1478 1479 // If the nodes are siblings, we can do a quick check 1480 } else if ( aup === bup ) { 1481 return siblingCheck( a, b ); 1482 } 1483 1484 // Otherwise we need full lists of their ancestors for comparison 1485 cur = a; 1486 while ( (cur = cur.parentNode) ) { 1487 ap.unshift( cur ); 1488 } 1489 cur = b; 1490 while ( (cur = cur.parentNode) ) { 1491 bp.unshift( cur ); 1492 } 1493 1494 // Walk down the tree looking for a discrepancy 1495 while ( ap[i] === bp[i] ) { 1496 i++; 1497 } 1498 1499 return i ? 1500 // Do a sibling check if the nodes have a common ancestor 1501 siblingCheck( ap[i], bp[i] ) : 1502 1503 // Otherwise nodes in our document sort first 1504 ap[i] === preferredDoc ? -1 : 1505 bp[i] === preferredDoc ? 1 : 1506 0; 1507 }; 1508 1509 return document; 1510}; 1511 1512Sizzle.matches = function( expr, elements ) { 1513 return Sizzle( expr, null, null, elements ); 1514}; 1515 1516Sizzle.matchesSelector = function( elem, expr ) { 1517 // Set document vars if needed 1518 if ( ( elem.ownerDocument || elem ) !== document ) { 1519 setDocument( elem ); 1520 } 1521 1522 // Make sure that attribute selectors are quoted 1523 expr = expr.replace( rattributeQuotes, "='$1']" ); 1524 1525 if ( support.matchesSelector && documentIsHTML && 1526 !compilerCache[ expr + " " ] && 1527 ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && 1528 ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { 1529 1530 try { 1531 var ret = matches.call( elem, expr ); 1532 1533 // IE 9's matchesSelector returns false on disconnected nodes 1534 if ( ret || support.disconnectedMatch || 1535 // As well, disconnected nodes are said to be in a document 1536 // fragment in IE 9 1537 elem.document && elem.document.nodeType !== 11 ) { 1538 return ret; 1539 } 1540 } catch (e) {} 1541 } 1542 1543 return Sizzle( expr, document, null, [ elem ] ).length > 0; 1544}; 1545 1546Sizzle.contains = function( context, elem ) { 1547 // Set document vars if needed 1548 if ( ( context.ownerDocument || context ) !== document ) { 1549 setDocument( context ); 1550 } 1551 return contains( context, elem ); 1552}; 1553 1554Sizzle.attr = function( elem, name ) { 1555 // Set document vars if needed 1556 if ( ( elem.ownerDocument || elem ) !== document ) { 1557 setDocument( elem ); 1558 } 1559 1560 var fn = Expr.attrHandle[ name.toLowerCase() ], 1561 // Don't get fooled by Object.prototype properties (jQuery #13807) 1562 val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? 1563 fn( elem, name, !documentIsHTML ) : 1564 undefined; 1565 1566 return val !== undefined ? 1567 val : 1568 support.attributes || !documentIsHTML ? 1569 elem.getAttribute( name ) : 1570 (val = elem.getAttributeNode(name)) && val.specified ? 1571 val.value : 1572 null; 1573}; 1574 1575Sizzle.escape = function( sel ) { 1576 return (sel + "").replace( rcssescape, fcssescape ); 1577}; 1578 1579Sizzle.error = function( msg ) { 1580 throw new Error( "Syntax error, unrecognized expression: " + msg ); 1581}; 1582 1583/** 1584 * Document sorting and removing duplicates 1585 * @param {ArrayLike} results 1586 */ 1587Sizzle.uniqueSort = function( results ) { 1588 var elem, 1589 duplicates = [], 1590 j = 0, 1591 i = 0; 1592 1593 // Unless we *know* we can detect duplicates, assume their presence 1594 hasDuplicate = !support.detectDuplicates; 1595 sortInput = !support.sortStable && results.slice( 0 ); 1596 results.sort( sortOrder ); 1597 1598 if ( hasDuplicate ) { 1599 while ( (elem = results[i++]) ) { 1600 if ( elem === results[ i ] ) { 1601 j = duplicates.push( i ); 1602 } 1603 } 1604 while ( j-- ) { 1605 results.splice( duplicates[ j ], 1 ); 1606 } 1607 } 1608 1609 // Clear input after sorting to release objects 1610 // See https://github.com/jquery/sizzle/pull/225 1611 sortInput = null; 1612 1613 return results; 1614}; 1615 1616/** 1617 * Utility function for retrieving the text value of an array of DOM nodes 1618 * @param {Array|Element} elem 1619 */ 1620getText = Sizzle.getText = function( elem ) { 1621 var node, 1622 ret = "", 1623 i = 0, 1624 nodeType = elem.nodeType; 1625 1626 if ( !nodeType ) { 1627 // If no nodeType, this is expected to be an array 1628 while ( (node = elem[i++]) ) {
vendor: 11,976 bytes, lines 1629-2027
1629 // Do not traverse comment nodes 1630 ret += getText( node ); 1631 } 1632 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { 1633 // Use textContent for elements 1634 // innerText usage removed for consistency of new lines (jQuery #11153) 1635 if ( typeof elem.textContent === "string" ) { 1636 return elem.textContent; 1637 } else { 1638 // Traverse its children 1639 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { 1640 ret += getText( elem ); 1641 } 1642 } 1643 } else if ( nodeType === 3 || nodeType === 4 ) { 1644 return elem.nodeValue; 1645 } 1646 // Do not include comment or processing instruction nodes 1647 1648 return ret; 1649}; 1650 1651Expr = Sizzle.selectors = { 1652 1653 // Can be adjusted by the user 1654 cacheLength: 50, 1655 1656 createPseudo: markFunction, 1657 1658 match: matchExpr, 1659 1660 attrHandle: {}, 1661 1662 find: {}, 1663 1664 relative: { 1665 ">": { dir: "parentNode", first: true }, 1666 " ": { dir: "parentNode" }, 1667 "+": { dir: "previousSibling", first: true }, 1668 "~": { dir: "previousSibling" } 1669 }, 1670 1671 preFilter: { 1672 "ATTR": function( match ) { 1673 match[1] = match[1].replace( runescape, funescape ); 1674 1675 // Move the given value to match[3] whether quoted or unquoted 1676 match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); 1677 1678 if ( match[2] === "~=" ) { 1679 match[3] = " " + match[3] + " "; 1680 } 1681 1682 return match.slice( 0, 4 ); 1683 }, 1684 1685 "CHILD": function( match ) { 1686 /* matches from matchExpr["CHILD"] 1687 1 type (only|nth|...) 1688 2 what (child|of-type) 1689 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 1690 4 xn-component of xn+y argument ([+-]?\d*n|) 1691 5 sign of xn-component 1692 6 x of xn-component 1693 7 sign of y-component 1694 8 y of y-component 1695 */ 1696 match[1] = match[1].toLowerCase(); 1697 1698 if ( match[1].slice( 0, 3 ) === "nth" ) { 1699 // nth-* requires argument 1700 if ( !match[3] ) { 1701 Sizzle.error( match[0] ); 1702 } 1703 1704 // numeric x and y parameters for Expr.filter.CHILD 1705 // remember that false/true cast respectively to 0/1 1706 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); 1707 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); 1708 1709 // other types prohibit arguments 1710 } else if ( match[3] ) { 1711 Sizzle.error( match[0] ); 1712 } 1713 1714 return match; 1715 }, 1716 1717 "PSEUDO": function( match ) { 1718 var excess, 1719 unquoted = !match[6] && match[2]; 1720 1721 if ( matchExpr["CHILD"].test( match[0] ) ) { 1722 return null; 1723 } 1724 1725 // Accept quoted arguments as-is 1726 if ( match[3] ) { 1727 match[2] = match[4] || match[5] || ""; 1728 1729 // Strip excess characters from unquoted arguments 1730 } else if ( unquoted && rpseudo.test( unquoted ) && 1731 // Get excess from tokenize (recursively) 1732 (excess = tokenize( unquoted, true )) && 1733 // advance to the next closing parenthesis 1734 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { 1735 1736 // excess is a negative index 1737 match[0] = match[0].slice( 0, excess ); 1738 match[2] = unquoted.slice( 0, excess ); 1739 } 1740 1741 // Return only captures needed by the pseudo filter method (type and argument) 1742 return match.slice( 0, 3 ); 1743 } 1744 }, 1745 1746 filter: { 1747 1748 "TAG": function( nodeNameSelector ) { 1749 var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); 1750 return nodeNameSelector === "*" ? 1751 function() { return true; } : 1752 function( elem ) { 1753 return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; 1754 }; 1755 }, 1756 1757 "CLASS": function( className ) { 1758 var pattern = classCache[ className + " " ]; 1759 1760 return pattern || 1761 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && 1762 classCache( className, function( elem ) { 1763 return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); 1764 }); 1765 }, 1766 1767 "ATTR": function( name, operator, check ) { 1768 return function( elem ) { 1769 var result = Sizzle.attr( elem, name ); 1770 1771 if ( result == null ) { 1772 return operator === "!="; 1773 } 1774 if ( !operator ) { 1775 return true; 1776 } 1777 1778 result += ""; 1779 1780 return operator === "=" ? result === check : 1781 operator === "!=" ? result !== check : 1782 operator === "^=" ? check && result.indexOf( check ) === 0 : 1783 operator === "*=" ? check && result.indexOf( check ) > -1 : 1784 operator === "$=" ? check && result.slice( -check.length ) === check : 1785 operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : 1786 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : 1787 false; 1788 }; 1789 }, 1790 1791 "CHILD": function( type, what, argument, first, last ) { 1792 var simple = type.slice( 0, 3 ) !== "nth", 1793 forward = type.slice( -4 ) !== "last", 1794 ofType = what === "of-type"; 1795 1796 return first === 1 && last === 0 ? 1797 1798 // Shortcut for :nth-*(n) 1799 function( elem ) { 1800 return !!elem.parentNode; 1801 } : 1802 1803 function( elem, context, xml ) { 1804 var cache, uniqueCache, outerCache, node, nodeIndex, start, 1805 dir = simple !== forward ? "nextSibling" : "previousSibling", 1806 parent = elem.parentNode, 1807 name = ofType && elem.nodeName.toLowerCase(), 1808 useCache = !xml && !ofType, 1809 diff = false; 1810 1811 if ( parent ) { 1812 1813 // :(first|last|only)-(child|of-type) 1814 if ( simple ) { 1815 while ( dir ) { 1816 node = elem; 1817 while ( (node = node[ dir ]) ) { 1818 if ( ofType ? 1819 node.nodeName.toLowerCase() === name : 1820 node.nodeType === 1 ) { 1821 1822 return false; 1823 } 1824 } 1825 // Reverse direction for :only-* (if we haven't yet done so) 1826 start = dir = type === "only" && !start && "nextSibling"; 1827 } 1828 return true; 1829 } 1830 1831 start = [ forward ? parent.firstChild : parent.lastChild ]; 1832 1833 // non-xml :nth-child(...) stores cache data on `parent` 1834 if ( forward && useCache ) { 1835 1836 // Seek `elem` from a previously-cached index 1837 1838 // ...in a gzip-friendly way 1839 node = parent; 1840 outerCache = node[ expando ] || (node[ expando ] = {}); 1841 1842 // Support: IE <9 only 1843 // Defend against cloned attroperties (jQuery gh-1709) 1844 uniqueCache = outerCache[ node.uniqueID ] || 1845 (outerCache[ node.uniqueID ] = {}); 1846 1847 cache = uniqueCache[ type ] || []; 1848 nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; 1849 diff = nodeIndex && cache[ 2 ]; 1850 node = nodeIndex && parent.childNodes[ nodeIndex ]; 1851 1852 while ( (node = ++nodeIndex && node && node[ dir ] || 1853 1854 // Fallback to seeking `elem` from the start 1855 (diff = nodeIndex = 0) || start.pop()) ) { 1856 1857 // When found, cache indexes on `parent` and break 1858 if ( node.nodeType === 1 && ++diff && node === elem ) { 1859 uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; 1860 break; 1861 } 1862 } 1863 1864 } else { 1865 // Use previously-cached element index if available 1866 if ( useCache ) { 1867 // ...in a gzip-friendly way 1868 node = elem; 1869 outerCache = node[ expando ] || (node[ expando ] = {}); 1870 1871 // Support: IE <9 only 1872 // Defend against cloned attroperties (jQuery gh-1709) 1873 uniqueCache = outerCache[ node.uniqueID ] || 1874 (outerCache[ node.uniqueID ] = {}); 1875 1876 cache = uniqueCache[ type ] || []; 1877 nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; 1878 diff = nodeIndex; 1879 } 1880 1881 // xml :nth-child(...) 1882 // or :nth-last-child(...) or :nth(-last)?-of-type(...) 1883 if ( diff === false ) { 1884 // Use the same loop as above to seek `elem` from the start 1885 while ( (node = ++nodeIndex && node && node[ dir ] || 1886 (diff = nodeIndex = 0) || start.pop()) ) { 1887 1888 if ( ( ofType ? 1889 node.nodeName.toLowerCase() === name : 1890 node.nodeType === 1 ) && 1891 ++diff ) { 1892 1893 // Cache the index of each encountered element 1894 if ( useCache ) { 1895 outerCache = node[ expando ] || (node[ expando ] = {}); 1896 1897 // Support: IE <9 only 1898 // Defend against cloned attroperties (jQuery gh-1709) 1899 uniqueCache = outerCache[ node.uniqueID ] || 1900 (outerCache[ node.uniqueID ] = {}); 1901 1902 uniqueCache[ type ] = [ dirruns, diff ]; 1903 } 1904 1905 if ( node === elem ) { 1906 break; 1907 } 1908 } 1909 } 1910 } 1911 } 1912 1913 // Incorporate the offset, then check against cycle size 1914 diff -= last; 1915 return diff === first || ( diff % first === 0 && diff / first >= 0 ); 1916 } 1917 }; 1918 }, 1919 1920 "PSEUDO": function( pseudo, argument ) { 1921 // pseudo-class names are case-insensitive 1922 // http://www.w3.org/TR/selectors/#pseudo-classes 1923 // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters 1924 // Remember that setFilters inherits from pseudos 1925 var args, 1926 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || 1927 Sizzle.error( "unsupported pseudo: " + pseudo ); 1928 1929 // The user may use createPseudo to indicate that 1930 // arguments are needed to create the filter function 1931 // just as Sizzle does 1932 if ( fn[ expando ] ) { 1933 return fn( argument ); 1934 } 1935 1936 // But maintain support for old signatures 1937 if ( fn.length > 1 ) { 1938 args = [ pseudo, pseudo, "", argument ]; 1939 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? 1940 markFunction(function( seed, matches ) { 1941 var idx, 1942 matched = fn( seed, argument ), 1943 i = matched.length; 1944 while ( i-- ) { 1945 idx = indexOf( seed, matched[i] ); 1946 seed[ idx ] = !( matches[ idx ] = matched[i] ); 1947 } 1948 }) : 1949 function( elem ) { 1950 return fn( elem, 0, args ); 1951 }; 1952 } 1953 1954 return fn; 1955 } 1956 }, 1957 1958 pseudos: { 1959 // Potentially complex pseudos 1960 "not": markFunction(function( selector ) { 1961 // Trim the selector passed to compile 1962 // to avoid treating leading and trailing 1963 // spaces as combinators 1964 var input = [], 1965 results = [], 1966 matcher = compile( selector.replace( rtrim, "$1" ) ); 1967 1968 return matcher[ expando ] ? 1969 markFunction(function( seed, matches, context, xml ) { 1970 var elem, 1971 unmatched = matcher( seed, null, xml, [] ), 1972 i = seed.length; 1973 1974 // Match elements unmatched by `matcher` 1975 while ( i-- ) { 1976 if ( (elem = unmatched[i]) ) { 1977 seed[i] = !(matches[i] = elem); 1978 } 1979 } 1980 }) : 1981 function( elem, context, xml ) { 1982 input[0] = elem; 1983 matcher( input, null, xml, results ); 1984 // Don't keep the element (issue #299) 1985 input[0] = null; 1986 return !results.pop(); 1987 }; 1988 }), 1989 1990 "has": markFunction(function( selector ) { 1991 return function( elem ) { 1992 return Sizzle( selector, elem ).length > 0; 1993 }; 1994 }), 1995 1996 "contains": markFunction(function( text ) { 1997 text = text.replace( runescape, funescape ); 1998 return function( elem ) { 1999 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; 2000 }; 2001 }), 2002 2003 // "Whether an element is represented by a :lang() selector 2004 // is based solely on the element's language value 2005 // being equal to the identifier C, 2006 // or beginning with the identifier C immediately followed by "-". 2007 // The matching of C against the element's language value is performed case-insensitively. 2008 // The identifier C does not have to be a valid language name." 2009 // http://www.w3.org/TR/selectors/#lang-pseudo 2010 "lang": markFunction( function( lang ) { 2011 // lang value must be a valid identifier 2012 if ( !ridentifier.test(lang || "") ) { 2013 Sizzle.error( "unsupported lang: " + lang ); 2014 } 2015 lang = lang.replace( runescape, funescape ).toLowerCase(); 2016 return function( elem ) { 2017 var elemLang; 2018 do { 2019 if ( (elemLang = documentIsHTML ? 2020 elem.lang : 2021 elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { 2022 2023 elemLang = elemLang.toLowerCase(); 2024 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; 2025 } 2026 } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); 2027 return false;
vendor: 22,724 bytes, lines 2028-2850
2028 }; 2029 }), 2030 2031 // Miscellaneous 2032 "target": function( elem ) { 2033 var hash = window.location && window.location.hash; 2034 return hash && hash.slice( 1 ) === elem.id; 2035 }, 2036 2037 "root": function( elem ) { 2038 return elem === docElem; 2039 }, 2040 2041 "focus": function( elem ) { 2042 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); 2043 }, 2044 2045 // Boolean properties 2046 "enabled": createDisabledPseudo( false ), 2047 "disabled": createDisabledPseudo( true ), 2048 2049 "checked": function( elem ) { 2050 // In CSS3, :checked should return both checked and selected elements 2051 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked 2052 var nodeName = elem.nodeName.toLowerCase(); 2053 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); 2054 }, 2055 2056 "selected": function( elem ) { 2057 // Accessing this property makes selected-by-default 2058 // options in Safari work properly 2059 if ( elem.parentNode ) { 2060 elem.parentNode.selectedIndex; 2061 } 2062 2063 return elem.selected === true; 2064 }, 2065 2066 // Contents 2067 "empty": function( elem ) { 2068 // http://www.w3.org/TR/selectors/#empty-pseudo 2069 // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), 2070 // but not by others (comment: 8; processing instruction: 7; etc.) 2071 // nodeType < 6 works because attributes (2) do not appear as children 2072 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { 2073 if ( elem.nodeType < 6 ) { 2074 return false; 2075 } 2076 } 2077 return true; 2078 }, 2079 2080 "parent": function( elem ) { 2081 return !Expr.pseudos["empty"]( elem ); 2082 }, 2083 2084 // Element/input types 2085 "header": function( elem ) { 2086 return rheader.test( elem.nodeName ); 2087 }, 2088 2089 "input": function( elem ) { 2090 return rinputs.test( elem.nodeName ); 2091 }, 2092 2093 "button": function( elem ) { 2094 var name = elem.nodeName.toLowerCase(); 2095 return name === "input" && elem.type === "button" || name === "button"; 2096 }, 2097 2098 "text": function( elem ) { 2099 var attr; 2100 return elem.nodeName.toLowerCase() === "input" && 2101 elem.type === "text" && 2102 2103 // Support: IE<8 2104 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" 2105 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); 2106 }, 2107 2108 // Position-in-collection 2109 "first": createPositionalPseudo(function() { 2110 return [ 0 ]; 2111 }), 2112 2113 "last": createPositionalPseudo(function( matchIndexes, length ) { 2114 return [ length - 1 ]; 2115 }), 2116 2117 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { 2118 return [ argument < 0 ? argument + length : argument ]; 2119 }), 2120 2121 "even": createPositionalPseudo(function( matchIndexes, length ) { 2122 var i = 0; 2123 for ( ; i < length; i += 2 ) { 2124 matchIndexes.push( i ); 2125 } 2126 return matchIndexes; 2127 }), 2128 2129 "odd": createPositionalPseudo(function( matchIndexes, length ) { 2130 var i = 1; 2131 for ( ; i < length; i += 2 ) { 2132 matchIndexes.push( i ); 2133 } 2134 return matchIndexes; 2135 }), 2136 2137 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { 2138 var i = argument < 0 ? argument + length : argument; 2139 for ( ; --i >= 0; ) { 2140 matchIndexes.push( i ); 2141 } 2142 return matchIndexes; 2143 }), 2144 2145 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { 2146 var i = argument < 0 ? argument + length : argument; 2147 for ( ; ++i < length; ) { 2148 matchIndexes.push( i ); 2149 } 2150 return matchIndexes; 2151 }) 2152 } 2153}; 2154 2155Expr.pseudos["nth"] = Expr.pseudos["eq"]; 2156 2157// Add button/input type pseudos 2158for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { 2159 Expr.pseudos[ i ] = createInputPseudo( i ); 2160} 2161for ( i in { submit: true, reset: true } ) { 2162 Expr.pseudos[ i ] = createButtonPseudo( i ); 2163} 2164 2165// Easy API for creating new setFilters 2166function setFilters() {} 2167setFilters.prototype = Expr.filters = Expr.pseudos; 2168Expr.setFilters = new setFilters(); 2169 2170tokenize = Sizzle.tokenize = function( selector, parseOnly ) { 2171 var matched, match, tokens, type, 2172 soFar, groups, preFilters, 2173 cached = tokenCache[ selector + " " ]; 2174 2175 if ( cached ) { 2176 return parseOnly ? 0 : cached.slice( 0 ); 2177 } 2178 2179 soFar = selector; 2180 groups = []; 2181 preFilters = Expr.preFilter; 2182 2183 while ( soFar ) { 2184 2185 // Comma and first run 2186 if ( !matched || (match = rcomma.exec( soFar )) ) { 2187 if ( match ) { 2188 // Don't consume trailing commas as valid 2189 soFar = soFar.slice( match[0].length ) || soFar; 2190 } 2191 groups.push( (tokens = []) ); 2192 } 2193 2194 matched = false; 2195 2196 // Combinators 2197 if ( (match = rcombinators.exec( soFar )) ) { 2198 matched = match.shift(); 2199 tokens.push({ 2200 value: matched, 2201 // Cast descendant combinators to space 2202 type: match[0].replace( rtrim, " " ) 2203 }); 2204 soFar = soFar.slice( matched.length ); 2205 } 2206 2207 // Filters 2208 for ( type in Expr.filter ) { 2209 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || 2210 (match = preFilters[ type ]( match ))) ) { 2211 matched = match.shift(); 2212 tokens.push({ 2213 value: matched, 2214 type: type, 2215 matches: match 2216 }); 2217 soFar = soFar.slice( matched.length ); 2218 } 2219 } 2220 2221 if ( !matched ) { 2222 break; 2223 } 2224 } 2225 2226 // Return the length of the invalid excess 2227 // if we're just parsing 2228 // Otherwise, throw an error or return tokens 2229 return parseOnly ? 2230 soFar.length : 2231 soFar ? 2232 Sizzle.error( selector ) : 2233 // Cache the tokens 2234 tokenCache( selector, groups ).slice( 0 ); 2235}; 2236 2237function toSelector( tokens ) { 2238 var i = 0, 2239 len = tokens.length, 2240 selector = ""; 2241 for ( ; i < len; i++ ) { 2242 selector += tokens[i].value; 2243 } 2244 return selector; 2245} 2246 2247function addCombinator( matcher, combinator, base ) { 2248 var dir = combinator.dir, 2249 skip = combinator.next, 2250 key = skip || dir, 2251 checkNonElements = base && key === "parentNode", 2252 doneName = done++; 2253 2254 return combinator.first ? 2255 // Check against closest ancestor/preceding element 2256 function( elem, context, xml ) { 2257 while ( (elem = elem[ dir ]) ) { 2258 if ( elem.nodeType === 1 || checkNonElements ) { 2259 return matcher( elem, context, xml ); 2260 } 2261 } 2262 return false; 2263 } : 2264 2265 // Check against all ancestor/preceding elements 2266 function( elem, context, xml ) { 2267 var oldCache, uniqueCache, outerCache, 2268 newCache = [ dirruns, doneName ]; 2269 2270 // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching 2271 if ( xml ) { 2272 while ( (elem = elem[ dir ]) ) { 2273 if ( elem.nodeType === 1 || checkNonElements ) { 2274 if ( matcher( elem, context, xml ) ) { 2275 return true; 2276 } 2277 } 2278 } 2279 } else { 2280 while ( (elem = elem[ dir ]) ) { 2281 if ( elem.nodeType === 1 || checkNonElements ) { 2282 outerCache = elem[ expando ] || (elem[ expando ] = {}); 2283 2284 // Support: IE <9 only 2285 // Defend against cloned attroperties (jQuery gh-1709) 2286 uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); 2287 2288 if ( skip && skip === elem.nodeName.toLowerCase() ) { 2289 elem = elem[ dir ] || elem; 2290 } else if ( (oldCache = uniqueCache[ key ]) && 2291 oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { 2292 2293 // Assign to newCache so results back-propagate to previous elements 2294 return (newCache[ 2 ] = oldCache[ 2 ]); 2295 } else { 2296 // Reuse newcache so results back-propagate to previous elements 2297 uniqueCache[ key ] = newCache; 2298 2299 // A match means we're done; a fail means we have to keep checking 2300 if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { 2301 return true; 2302 } 2303 } 2304 } 2305 } 2306 } 2307 return false; 2308 }; 2309} 2310 2311function elementMatcher( matchers ) { 2312 return matchers.length > 1 ? 2313 function( elem, context, xml ) { 2314 var i = matchers.length; 2315 while ( i-- ) { 2316 if ( !matchers[i]( elem, context, xml ) ) { 2317 return false; 2318 } 2319 } 2320 return true; 2321 } : 2322 matchers[0]; 2323} 2324 2325function multipleContexts( selector, contexts, results ) { 2326 var i = 0, 2327 len = contexts.length; 2328 for ( ; i < len; i++ ) { 2329 Sizzle( selector, contexts[i], results ); 2330 } 2331 return results; 2332} 2333 2334function condense( unmatched, map, filter, context, xml ) { 2335 var elem, 2336 newUnmatched = [], 2337 i = 0, 2338 len = unmatched.length, 2339 mapped = map != null; 2340 2341 for ( ; i < len; i++ ) { 2342 if ( (elem = unmatched[i]) ) { 2343 if ( !filter || filter( elem, context, xml ) ) { 2344 newUnmatched.push( elem ); 2345 if ( mapped ) { 2346 map.push( i ); 2347 } 2348 } 2349 } 2350 } 2351 2352 return newUnmatched; 2353} 2354 2355function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { 2356 if ( postFilter && !postFilter[ expando ] ) { 2357 postFilter = setMatcher( postFilter ); 2358 } 2359 if ( postFinder && !postFinder[ expando ] ) { 2360 postFinder = setMatcher( postFinder, postSelector ); 2361 } 2362 return markFunction(function( seed, results, context, xml ) { 2363 var temp, i, elem, 2364 preMap = [], 2365 postMap = [], 2366 preexisting = results.length, 2367 2368 // Get initial elements from seed or context 2369 elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), 2370 2371 // Prefilter to get matcher input, preserving a map for seed-results synchronization 2372 matcherIn = preFilter && ( seed || !selector ) ? 2373 condense( elems, preMap, preFilter, context, xml ) : 2374 elems, 2375 2376 matcherOut = matcher ? 2377 // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, 2378 postFinder || ( seed ? preFilter : preexisting || postFilter ) ? 2379 2380 // ...intermediate processing is necessary 2381 [] : 2382 2383 // ...otherwise use results directly 2384 results : 2385 matcherIn; 2386 2387 // Find primary matches 2388 if ( matcher ) { 2389 matcher( matcherIn, matcherOut, context, xml ); 2390 } 2391 2392 // Apply postFilter 2393 if ( postFilter ) { 2394 temp = condense( matcherOut, postMap ); 2395 postFilter( temp, [], context, xml ); 2396 2397 // Un-match failing elements by moving them back to matcherIn 2398 i = temp.length; 2399 while ( i-- ) { 2400 if ( (elem = temp[i]) ) { 2401 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); 2402 } 2403 } 2404 } 2405 2406 if ( seed ) { 2407 if ( postFinder || preFilter ) { 2408 if ( postFinder ) { 2409 // Get the final matcherOut by condensing this intermediate into postFinder contexts 2410 temp = []; 2411 i = matcherOut.length; 2412 while ( i-- ) { 2413 if ( (elem = matcherOut[i]) ) { 2414 // Restore matcherIn since elem is not yet a final match 2415 temp.push( (matcherIn[i] = elem) ); 2416 } 2417 } 2418 postFinder( null, (matcherOut = []), temp, xml ); 2419 } 2420 2421 // Move matched elements from seed to results to keep them synchronized 2422 i = matcherOut.length; 2423 while ( i-- ) { 2424 if ( (elem = matcherOut[i]) && 2425 (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { 2426 2427 seed[temp] = !(results[temp] = elem); 2428 } 2429 } 2430 } 2431 2432 // Add elements to results, through postFinder if defined 2433 } else { 2434 matcherOut = condense( 2435 matcherOut === results ? 2436 matcherOut.splice( preexisting, matcherOut.length ) : 2437 matcherOut 2438 ); 2439 if ( postFinder ) { 2440 postFinder( null, results, matcherOut, xml ); 2441 } else { 2442 push.apply( results, matcherOut ); 2443 } 2444 } 2445 }); 2446} 2447 2448function matcherFromTokens( tokens ) { 2449 var checkContext, matcher, j, 2450 len = tokens.length, 2451 leadingRelative = Expr.relative[ tokens[0].type ], 2452 implicitRelative = leadingRelative || Expr.relative[" "], 2453 i = leadingRelative ? 1 : 0, 2454 2455 // The foundational matcher ensures that elements are reachable from top-level context(s) 2456 matchContext = addCombinator( function( elem ) { 2457 return elem === checkContext; 2458 }, implicitRelative, true ), 2459 matchAnyContext = addCombinator( function( elem ) { 2460 return indexOf( checkContext, elem ) > -1; 2461 }, implicitRelative, true ), 2462 matchers = [ function( elem, context, xml ) { 2463 var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( 2464 (checkContext = context).nodeType ? 2465 matchContext( elem, context, xml ) : 2466 matchAnyContext( elem, context, xml ) ); 2467 // Avoid hanging onto element (issue #299) 2468 checkContext = null; 2469 return ret; 2470 } ]; 2471 2472 for ( ; i < len; i++ ) { 2473 if ( (matcher = Expr.relative[ tokens[i].type ]) ) { 2474 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; 2475 } else { 2476 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); 2477 2478 // Return special upon seeing a positional matcher 2479 if ( matcher[ expando ] ) { 2480 // Find the next relative operator (if any) for proper handling 2481 j = ++i; 2482 for ( ; j < len; j++ ) { 2483 if ( Expr.relative[ tokens[j].type ] ) { 2484 break; 2485 } 2486 } 2487 return setMatcher( 2488 i > 1 && elementMatcher( matchers ), 2489 i > 1 && toSelector( 2490 // If the preceding token was a descendant combinator, insert an implicit any-element `*` 2491 tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) 2492 ).replace( rtrim, "$1" ), 2493 matcher, 2494 i < j && matcherFromTokens( tokens.slice( i, j ) ), 2495 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), 2496 j < len && toSelector( tokens ) 2497 ); 2498 } 2499 matchers.push( matcher ); 2500 } 2501 } 2502 2503 return elementMatcher( matchers ); 2504} 2505 2506function matcherFromGroupMatchers( elementMatchers, setMatchers ) { 2507 var bySet = setMatchers.length > 0, 2508 byElement = elementMatchers.length > 0, 2509 superMatcher = function( seed, context, xml, results, outermost ) { 2510 var elem, j, matcher, 2511 matchedCount = 0, 2512 i = "0", 2513 unmatched = seed && [], 2514 setMatched = [], 2515 contextBackup = outermostContext, 2516 // We must always have either seed elements or outermost context 2517 elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), 2518 // Use integer dirruns iff this is the outermost matcher 2519 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), 2520 len = elems.length; 2521 2522 if ( outermost ) { 2523 outermostContext = context === document || context || outermost; 2524 } 2525 2526 // Add elements passing elementMatchers directly to results 2527 // Support: IE<9, Safari 2528 // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id 2529 for ( ; i !== len && (elem = elems[i]) != null; i++ ) { 2530 if ( byElement && elem ) { 2531 j = 0; 2532 if ( !context && elem.ownerDocument !== document ) { 2533 setDocument( elem ); 2534 xml = !documentIsHTML; 2535 } 2536 while ( (matcher = elementMatchers[j++]) ) { 2537 if ( matcher( elem, context || document, xml) ) { 2538 results.push( elem ); 2539 break; 2540 } 2541 } 2542 if ( outermost ) { 2543 dirruns = dirrunsUnique; 2544 } 2545 } 2546 2547 // Track unmatched elements for set filters 2548 if ( bySet ) { 2549 // They will have gone through all possible matchers 2550 if ( (elem = !matcher && elem) ) { 2551 matchedCount--; 2552 } 2553 2554 // Lengthen the array for every element, matched or not 2555 if ( seed ) { 2556 unmatched.push( elem ); 2557 } 2558 } 2559 } 2560 2561 // `i` is now the count of elements visited above, and adding it to `matchedCount` 2562 // makes the latter nonnegative. 2563 matchedCount += i; 2564 2565 // Apply set filters to unmatched elements 2566 // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` 2567 // equals `i`), unless we didn't visit _any_ elements in the above loop because we have 2568 // no element matchers and no seed. 2569 // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that 2570 // case, which will result in a "00" `matchedCount` that differs from `i` but is also 2571 // numerically zero. 2572 if ( bySet && i !== matchedCount ) { 2573 j = 0; 2574 while ( (matcher = setMatchers[j++]) ) { 2575 matcher( unmatched, setMatched, context, xml ); 2576 } 2577 2578 if ( seed ) { 2579 // Reintegrate element matches to eliminate the need for sorting 2580 if ( matchedCount > 0 ) { 2581 while ( i-- ) { 2582 if ( !(unmatched[i] || setMatched[i]) ) { 2583 setMatched[i] = pop.call( results ); 2584 } 2585 } 2586 } 2587 2588 // Discard index placeholder values to get only actual matches 2589 setMatched = condense( setMatched ); 2590 } 2591 2592 // Add matches to results 2593 push.apply( results, setMatched ); 2594 2595 // Seedless set matches succeeding multiple successful matchers stipulate sorting 2596 if ( outermost && !seed && setMatched.length > 0 && 2597 ( matchedCount + setMatchers.length ) > 1 ) { 2598 2599 Sizzle.uniqueSort( results ); 2600 } 2601 } 2602 2603 // Override manipulation of globals by nested matchers 2604 if ( outermost ) { 2605 dirruns = dirrunsUnique; 2606 outermostContext = contextBackup; 2607 } 2608 2609 return unmatched; 2610 }; 2611 2612 return bySet ? 2613 markFunction( superMatcher ) : 2614 superMatcher; 2615} 2616 2617compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { 2618 var i, 2619 setMatchers = [], 2620 elementMatchers = [], 2621 cached = compilerCache[ selector + " " ]; 2622 2623 if ( !cached ) { 2624 // Generate a function of recursive functions that can be used to check each element 2625 if ( !match ) { 2626 match = tokenize( selector ); 2627 } 2628 i = match.length; 2629 while ( i-- ) { 2630 cached = matcherFromTokens( match[i] ); 2631 if ( cached[ expando ] ) { 2632 setMatchers.push( cached ); 2633 } else { 2634 elementMatchers.push( cached ); 2635 } 2636 } 2637 2638 // Cache the compiled function 2639 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); 2640 2641 // Save selector and tokenization 2642 cached.selector = selector; 2643 } 2644 return cached; 2645}; 2646 2647/** 2648 * A low-level selection function that works with Sizzle's compiled 2649 * selector functions 2650 * @param {String|Function} selector A selector or a pre-compiled 2651 * selector function built with Sizzle.compile 2652 * @param {Element} context 2653 * @param {Array} [results] 2654 * @param {Array} [seed] A set of elements to match against 2655 */ 2656select = Sizzle.select = function( selector, context, results, seed ) { 2657 var i, tokens, token, type, find, 2658 compiled = typeof selector === "function" && selector, 2659 match = !seed && tokenize( (selector = compiled.selector || selector) ); 2660 2661 results = results || []; 2662 2663 // Try to minimize operations if there is only one selector in the list and no seed 2664 // (the latter of which guarantees us context) 2665 if ( match.length === 1 ) { 2666 2667 // Reduce context if the leading compound selector is an ID 2668 tokens = match[0] = match[0].slice( 0 ); 2669 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && 2670 context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { 2671 2672 context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; 2673 if ( !context ) { 2674 return results; 2675 2676 // Precompiled matchers will still verify ancestry, so step up a level 2677 } else if ( compiled ) { 2678 context = context.parentNode; 2679 } 2680 2681 selector = selector.slice( tokens.shift().value.length ); 2682 } 2683 2684 // Fetch a seed set for right-to-left matching 2685 i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; 2686 while ( i-- ) { 2687 token = tokens[i]; 2688 2689 // Abort if we hit a combinator 2690 if ( Expr.relative[ (type = token.type) ] ) { 2691 break; 2692 } 2693 if ( (find = Expr.find[ type ]) ) { 2694 // Search, expanding context for leading sibling combinators 2695 if ( (seed = find( 2696 token.matches[0].replace( runescape, funescape ), 2697 rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context 2698 )) ) { 2699 2700 // If seed is empty or no tokens remain, we can return early 2701 tokens.splice( i, 1 ); 2702 selector = seed.length && toSelector( tokens ); 2703 if ( !selector ) { 2704 push.apply( results, seed ); 2705 return results; 2706 } 2707 2708 break; 2709 } 2710 } 2711 } 2712 } 2713 2714 // Compile and execute a filtering function if one is not provided 2715 // Provide `match` to avoid retokenization if we modified the selector above 2716 ( compiled || compile( selector, match ) )( 2717 seed, 2718 context, 2719 !documentIsHTML, 2720 results, 2721 !context || rsibling.test( selector ) && testContext( context.parentNode ) || context 2722 ); 2723 return results; 2724}; 2725 2726// One-time assignments 2727 2728// Sort stability 2729support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; 2730 2731// Support: Chrome 14-35+ 2732// Always assume duplicates if they aren't passed to the comparison function 2733support.detectDuplicates = !!hasDuplicate; 2734 2735// Initialize against the default document 2736setDocument(); 2737 2738// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) 2739// Detached nodes confoundingly follow *each other* 2740support.sortDetached = assert(function( el ) { 2741 // Should return 1, but returns 4 (following) 2742 return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; 2743}); 2744 2745// Support: IE<8 2746// Prevent attribute/property "interpolation" 2747// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx 2748if ( !assert(function( el ) { 2749 el.innerHTML = "<a href='#'></a>"; 2750 return el.firstChild.getAttribute("href") === "#" ; 2751}) ) { 2752 addHandle( "type|href|height|width", function( elem, name, isXML ) { 2753 if ( !isXML ) { 2754 return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); 2755 } 2756 }); 2757} 2758 2759// Support: IE<9 2760// Use defaultValue in place of getAttribute("value") 2761if ( !support.attributes || !assert(function( el ) { 2762 el.innerHTML = "<input/>"; 2763 el.firstChild.setAttribute( "value", "" ); 2764 return el.firstChild.getAttribute( "value" ) === ""; 2765}) ) { 2766 addHandle( "value", function( elem, name, isXML ) { 2767 if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { 2768 return elem.defaultValue; 2769 } 2770 }); 2771} 2772 2773// Support: IE<9 2774// Use getAttributeNode to fetch booleans when getAttribute lies 2775if ( !assert(function( el ) { 2776 return el.getAttribute("disabled") == null; 2777}) ) { 2778 addHandle( booleans, function( elem, name, isXML ) { 2779 var val; 2780 if ( !isXML ) { 2781 return elem[ name ] === true ? name.toLowerCase() : 2782 (val = elem.getAttributeNode( name )) && val.specified ? 2783 val.value : 2784 null; 2785 } 2786 }); 2787} 2788 2789return Sizzle; 2790 2791})( window ); 2792 2793 2794 2795jQuery.find = Sizzle; 2796jQuery.expr = Sizzle.selectors; 2797 2798// Deprecated 2799jQuery.expr[ ":" ] = jQuery.expr.pseudos; 2800jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; 2801jQuery.text = Sizzle.getText; 2802jQuery.isXMLDoc = Sizzle.isXML; 2803jQuery.contains = Sizzle.contains; 2804jQuery.escapeSelector = Sizzle.escape; 2805 2806 2807 2808 2809var dir = function( elem, dir, until ) { 2810 var matched = [], 2811 truncate = until !== undefined; 2812 2813 while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { 2814 if ( elem.nodeType === 1 ) { 2815 if ( truncate && jQuery( elem ).is( until ) ) { 2816 break; 2817 } 2818 matched.push( elem ); 2819 } 2820 } 2821 return matched; 2822}; 2823 2824 2825var siblings = function( n, elem ) { 2826 var matched = []; 2827 2828 for ( ; n; n = n.nextSibling ) { 2829 if ( n.nodeType === 1 && n !== elem ) { 2830 matched.push( n ); 2831 } 2832 } 2833 2834 return matched; 2835}; 2836 2837 2838var rneedsContext = jQuery.expr.match.needsContext; 2839 2840 2841 2842function nodeName( elem, name ) { 2843 2844 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); 2845 2846}; 2847var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); 2848 2849 2850
vendor: 1,441 bytes, lines 2851-2899
2851var risSimple = /^.[^:#\[\.,]*$/; 2852 2853// Implement the identical functionality for filter and not 2854function winnow( elements, qualifier, not ) { 2855 if ( jQuery.isFunction( qualifier ) ) { 2856 return jQuery.grep( elements, function( elem, i ) { 2857 return !!qualifier.call( elem, i, elem ) !== not; 2858 } ); 2859 } 2860 2861 // Single element 2862 if ( qualifier.nodeType ) { 2863 return jQuery.grep( elements, function( elem ) { 2864 return ( elem === qualifier ) !== not; 2865 } ); 2866 } 2867 2868 // Arraylike of elements (jQuery, arguments, Array) 2869 if ( typeof qualifier !== "string" ) { 2870 return jQuery.grep( elements, function( elem ) { 2871 return ( indexOf.call( qualifier, elem ) > -1 ) !== not; 2872 } ); 2873 } 2874 2875 // Simple selector that can be filtered directly, removing non-Elements 2876 if ( risSimple.test( qualifier ) ) { 2877 return jQuery.filter( qualifier, elements, not ); 2878 } 2879 2880 // Complex selector, compare the two sets, removing non-Elements 2881 qualifier = jQuery.filter( qualifier, elements ); 2882 return jQuery.grep( elements, function( elem ) { 2883 return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1; 2884 } ); 2885} 2886 2887jQuery.filter = function( expr, elems, not ) { 2888 var elem = elems[ 0 ]; 2889 2890 if ( not ) { 2891 expr = ":not(" + expr + ")"; 2892 } 2893 2894 if ( elems.length === 1 && elem.nodeType === 1 ) { 2895 return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; 2896 } 2897 2898 return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { 2899 return elem.nodeType === 1;
vendor: 4,831 bytes, lines 2900-3095
2900 } ) ); 2901}; 2902 2903jQuery.fn.extend( { 2904 find: function( selector ) { 2905 var i, ret, 2906 len = this.length, 2907 self = this; 2908 2909 if ( typeof selector !== "string" ) { 2910 return this.pushStack( jQuery( selector ).filter( function() { 2911 for ( i = 0; i < len; i++ ) { 2912 if ( jQuery.contains( self[ i ], this ) ) { 2913 return true; 2914 } 2915 } 2916 } ) ); 2917 } 2918 2919 ret = this.pushStack( [] ); 2920 2921 for ( i = 0; i < len; i++ ) { 2922 jQuery.find( selector, self[ i ], ret ); 2923 } 2924 2925 return len > 1 ? jQuery.uniqueSort( ret ) : ret; 2926 }, 2927 filter: function( selector ) { 2928 return this.pushStack( winnow( this, selector || [], false ) ); 2929 }, 2930 not: function( selector ) { 2931 return this.pushStack( winnow( this, selector || [], true ) ); 2932 }, 2933 is: function( selector ) { 2934 return !!winnow( 2935 this, 2936 2937 // If this is a positional/relative selector, check membership in the returned set 2938 // so $("p:first").is("p:last") won't return true for a doc with two "p". 2939 typeof selector === "string" && rneedsContext.test( selector ) ? 2940 jQuery( selector ) : 2941 selector || [], 2942 false 2943 ).length; 2944 } 2945} ); 2946 2947 2948// Initialize a jQuery object 2949 2950 2951// A central reference to the root jQuery(document) 2952var rootjQuery, 2953 2954 // A simple way to check for HTML strings 2955 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) 2956 // Strict HTML recognition (#11290: must start with <) 2957 // Shortcut simple #id case for speed 2958 rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, 2959 2960 init = jQuery.fn.init = function( selector, context, root ) { 2961 var match, elem; 2962 2963 // HANDLE: $(""), $(null), $(undefined), $(false) 2964 if ( !selector ) { 2965 return this; 2966 } 2967 2968 // Method init() accepts an alternate rootjQuery 2969 // so migrate can support jQuery.sub (gh-2101) 2970 root = root || rootjQuery; 2971 2972 // Handle HTML strings 2973 if ( typeof selector === "string" ) { 2974 if ( selector[ 0 ] === "<" && 2975 selector[ selector.length - 1 ] === ">" && 2976 selector.length >= 3 ) { 2977 2978 // Assume that strings that start and end with <> are HTML and skip the regex check 2979 match = [ null, selector, null ]; 2980 2981 } else { 2982 match = rquickExpr.exec( selector ); 2983 } 2984 2985 // Match html or make sure no context is specified for #id 2986 if ( match && ( match[ 1 ] || !context ) ) { 2987 2988 // HANDLE: $(html) -> $(array) 2989 if ( match[ 1 ] ) { 2990 context = context instanceof jQuery ? context[ 0 ] : context; 2991 2992 // Option to run scripts is true for back-compat 2993 // Intentionally let the error be thrown if parseHTML is not present 2994 jQuery.merge( this, jQuery.parseHTML( 2995 match[ 1 ], 2996 context && context.nodeType ? context.ownerDocument || context : document, 2997 true 2998 ) ); 2999 3000 // HANDLE: $(html, props) 3001 if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { 3002 for ( match in context ) { 3003 3004 // Properties of context are called as methods if possible 3005 if ( jQuery.isFunction( this[ match ] ) ) { 3006 this[ match ]( context[ match ] ); 3007 3008 // ...and otherwise set as attributes 3009 } else { 3010 this.attr( match, context[ match ] ); 3011 } 3012 } 3013 } 3014 3015 return this; 3016 3017 // HANDLE: $(#id) 3018 } else { 3019 elem = document.getElementById( match[ 2 ] ); 3020 3021 if ( elem ) { 3022 3023 // Inject the element directly into the jQuery object 3024 this[ 0 ] = elem; 3025 this.length = 1; 3026 } 3027 return this; 3028 } 3029 3030 // HANDLE: $(expr, $(...)) 3031 } else if ( !context || context.jquery ) { 3032 return ( context || root ).find( selector ); 3033 3034 // HANDLE: $(expr, context) 3035 // (which is just equivalent to: $(context).find(expr) 3036 } else { 3037 return this.constructor( context ).find( selector ); 3038 } 3039 3040 // HANDLE: $(DOMElement) 3041 } else if ( selector.nodeType ) { 3042 this[ 0 ] = selector; 3043 this.length = 1; 3044 return this; 3045 3046 // HANDLE: $(function) 3047 // Shortcut for document ready 3048 } else if ( jQuery.isFunction( selector ) ) { 3049 return root.ready !== undefined ? 3050 root.ready( selector ) : 3051 3052 // Execute immediately if ready is not present 3053 selector( jQuery ); 3054 } 3055 3056 return jQuery.makeArray( selector, this ); 3057 }; 3058 3059// Give the init function the jQuery prototype for later instantiation 3060init.prototype = jQuery.fn; 3061 3062// Initialize central reference 3063rootjQuery = jQuery( document ); 3064 3065 3066var rparentsprev = /^(?:parents|prev(?:Until|All))/, 3067 3068 // Methods guaranteed to produce a unique set when starting from a unique set 3069 guaranteedUnique = { 3070 children: true, 3071 contents: true, 3072 next: true, 3073 prev: true 3074 }; 3075 3076jQuery.fn.extend( { 3077 has: function( target ) { 3078 var targets = jQuery( target, this ), 3079 l = targets.length; 3080 3081 return this.filter( function() { 3082 var i = 0; 3083 for ( ; i < l; i++ ) { 3084 if ( jQuery.contains( this, targets[ i ] ) ) { 3085 return true; 3086 } 3087 } 3088 } ); 3089 }, 3090 3091 closest: function( selectors, context ) { 3092 var cur, 3093 i = 0, 3094 l = this.length, 3095 matched = [],
vendor: 6,797 bytes, lines 3096-3363
3096 targets = typeof selectors !== "string" && jQuery( selectors ); 3097 3098 // Positional selectors never match, since there's no _selection_ context 3099 if ( !rneedsContext.test( selectors ) ) { 3100 for ( ; i < l; i++ ) { 3101 for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { 3102 3103 // Always skip document fragments 3104 if ( cur.nodeType < 11 && ( targets ? 3105 targets.index( cur ) > -1 : 3106 3107 // Don't pass non-elements to Sizzle 3108 cur.nodeType === 1 && 3109 jQuery.find.matchesSelector( cur, selectors ) ) ) { 3110 3111 matched.push( cur ); 3112 break; 3113 } 3114 } 3115 } 3116 } 3117 3118 return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); 3119 }, 3120 3121 // Determine the position of an element within the set 3122 index: function( elem ) { 3123 3124 // No argument, return index in parent 3125 if ( !elem ) { 3126 return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; 3127 } 3128 3129 // Index in selector 3130 if ( typeof elem === "string" ) { 3131 return indexOf.call( jQuery( elem ), this[ 0 ] ); 3132 } 3133 3134 // Locate the position of the desired element 3135 return indexOf.call( this, 3136 3137 // If it receives a jQuery object, the first element is used 3138 elem.jquery ? elem[ 0 ] : elem 3139 ); 3140 }, 3141 3142 add: function( selector, context ) { 3143 return this.pushStack( 3144 jQuery.uniqueSort( 3145 jQuery.merge( this.get(), jQuery( selector, context ) ) 3146 ) 3147 ); 3148 }, 3149 3150 addBack: function( selector ) { 3151 return this.add( selector == null ? 3152 this.prevObject : this.prevObject.filter( selector ) 3153 ); 3154 } 3155} ); 3156 3157function sibling( cur, dir ) { 3158 while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} 3159 return cur; 3160} 3161 3162jQuery.each( { 3163 parent: function( elem ) { 3164 var parent = elem.parentNode; 3165 return parent && parent.nodeType !== 11 ? parent : null; 3166 }, 3167 parents: function( elem ) { 3168 return dir( elem, "parentNode" ); 3169 }, 3170 parentsUntil: function( elem, i, until ) { 3171 return dir( elem, "parentNode", until ); 3172 }, 3173 next: function( elem ) { 3174 return sibling( elem, "nextSibling" ); 3175 }, 3176 prev: function( elem ) { 3177 return sibling( elem, "previousSibling" ); 3178 }, 3179 nextAll: function( elem ) { 3180 return dir( elem, "nextSibling" ); 3181 }, 3182 prevAll: function( elem ) { 3183 return dir( elem, "previousSibling" ); 3184 }, 3185 nextUntil: function( elem, i, until ) { 3186 return dir( elem, "nextSibling", until ); 3187 }, 3188 prevUntil: function( elem, i, until ) { 3189 return dir( elem, "previousSibling", until ); 3190 }, 3191 siblings: function( elem ) { 3192 return siblings( ( elem.parentNode || {} ).firstChild, elem ); 3193 }, 3194 children: function( elem ) { 3195 return siblings( elem.firstChild ); 3196 }, 3197 contents: function( elem ) { 3198 if ( nodeName( elem, "iframe" ) ) { 3199 return elem.contentDocument; 3200 } 3201 3202 // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only 3203 // Treat the template element as a regular one in browsers that 3204 // don't support it. 3205 if ( nodeName( elem, "template" ) ) { 3206 elem = elem.content || elem; 3207 } 3208 3209 return jQuery.merge( [], elem.childNodes ); 3210 } 3211}, function( name, fn ) { 3212 jQuery.fn[ name ] = function( until, selector ) { 3213 var matched = jQuery.map( this, fn, until ); 3214 3215 if ( name.slice( -5 ) !== "Until" ) { 3216 selector = until; 3217 } 3218 3219 if ( selector && typeof selector === "string" ) { 3220 matched = jQuery.filter( selector, matched ); 3221 } 3222 3223 if ( this.length > 1 ) { 3224 3225 // Remove duplicates 3226 if ( !guaranteedUnique[ name ] ) { 3227 jQuery.uniqueSort( matched ); 3228 } 3229 3230 // Reverse order for parents* and prev-derivatives 3231 if ( rparentsprev.test( name ) ) { 3232 matched.reverse(); 3233 } 3234 } 3235 3236 return this.pushStack( matched ); 3237 }; 3238} ); 3239var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); 3240 3241 3242 3243// Convert String-formatted options into Object-formatted ones 3244function createOptions( options ) { 3245 var object = {}; 3246 jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { 3247 object[ flag ] = true; 3248 } ); 3249 return object; 3250} 3251 3252/* 3253 * Create a callback list using the following parameters: 3254 * 3255 * options: an optional list of space-separated options that will change how 3256 * the callback list behaves or a more traditional option object 3257 * 3258 * By default a callback list will act like an event callback list and can be 3259 * "fired" multiple times. 3260 * 3261 * Possible options: 3262 * 3263 * once: will ensure the callback list can only be fired once (like a Deferred) 3264 * 3265 * memory: will keep track of previous values and will call any callback added 3266 * after the list has been fired right away with the latest "memorized" 3267 * values (like a Deferred) 3268 * 3269 * unique: will ensure a callback can only be added once (no duplicate in the list) 3270 * 3271 * stopOnFalse: interrupt callings when a callback returns false 3272 * 3273 */ 3274jQuery.Callbacks = function( options ) { 3275 3276 // Convert options from String-formatted to Object-formatted if needed 3277 // (we check in cache first) 3278 options = typeof options === "string" ? 3279 createOptions( options ) : 3280 jQuery.extend( {}, options ); 3281 3282 var // Flag to know if list is currently firing 3283 firing, 3284 3285 // Last fire value for non-forgettable lists 3286 memory, 3287 3288 // Flag to know if list was already fired 3289 fired, 3290 3291 // Flag to prevent firing 3292 locked, 3293 3294 // Actual callback list 3295 list = [], 3296 3297 // Queue of execution data for repeatable lists 3298 queue = [], 3299 3300 // Index of currently firing callback (modified by add/remove as needed) 3301 firingIndex = -1, 3302 3303 // Fire callbacks 3304 fire = function() { 3305 3306 // Enforce single-firing 3307 locked = locked || options.once; 3308 3309 // Execute callbacks for all pending executions, 3310 // respecting firingIndex overrides and runtime changes 3311 fired = firing = true; 3312 for ( ; queue.length; firingIndex = -1 ) { 3313 memory = queue.shift(); 3314 while ( ++firingIndex < list.length ) { 3315 3316 // Run callback and check for early termination 3317 if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && 3318 options.stopOnFalse ) { 3319 3320 // Jump to end and forget the data so .add doesn't re-fire 3321 firingIndex = list.length; 3322 memory = false; 3323 } 3324 } 3325 } 3326 3327 // Forget the data if we're done with it 3328 if ( !options.memory ) { 3329 memory = false; 3330 } 3331 3332 firing = false; 3333 3334 // Clean up if we're done firing for good 3335 if ( locked ) { 3336 3337 // Keep an empty list if we have data for future add calls 3338 if ( memory ) { 3339 list = []; 3340 3341 // Otherwise, this object is spent 3342 } else { 3343 list = ""; 3344 } 3345 } 3346 }, 3347 3348 // Actual Callbacks object 3349 self = { 3350 3351 // Add a callback or a collection of callbacks to the list 3352 add: function() { 3353 if ( list ) { 3354 3355 // If we have memory from a past run, we should fire after adding 3356 if ( memory && !firing ) { 3357 firingIndex = list.length - 1; 3358 queue.push( memory ); 3359 } 3360 3361 ( function add( args ) { 3362 jQuery.each( args, function( _, arg ) { 3363 if (
vendor: 3,423 bytes, lines 3363-3505
3363jQuery.isFunction( arg ) ) { 3364 if ( !options.unique || !self.has( arg ) ) { 3365 list.push( arg ); 3366 } 3367 } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { 3368 3369 // Inspect recursively 3370 add( arg ); 3371 } 3372 } ); 3373 } )( arguments ); 3374 3375 if ( memory && !firing ) { 3376 fire(); 3377 } 3378 } 3379 return this; 3380 }, 3381 3382 // Remove a callback from the list 3383 remove: function() { 3384 jQuery.each( arguments, function( _, arg ) { 3385 var index; 3386 while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { 3387 list.splice( index, 1 ); 3388 3389 // Handle firing indexes 3390 if ( index <= firingIndex ) { 3391 firingIndex--; 3392 } 3393 } 3394 } ); 3395 return this; 3396 }, 3397 3398 // Check if a given callback is in the list. 3399 // If no argument is given, return whether or not list has callbacks attached. 3400 has: function( fn ) { 3401 return fn ? 3402 jQuery.inArray( fn, list ) > -1 : 3403 list.length > 0; 3404 }, 3405 3406 // Remove all callbacks from the list 3407 empty: function() { 3408 if ( list ) { 3409 list = []; 3410 } 3411 return this; 3412 }, 3413 3414 // Disable .fire and .add 3415 // Abort any current/pending executions 3416 // Clear all callbacks and values 3417 disable: function() { 3418 locked = queue = []; 3419 list = memory = ""; 3420 return this; 3421 }, 3422 disabled: function() { 3423 return !list; 3424 }, 3425 3426 // Disable .fire 3427 // Also disable .add unless we have memory (since it would have no effect) 3428 // Abort any pending executions 3429 lock: function() { 3430 locked = queue = []; 3431 if ( !memory && !firing ) { 3432 list = memory = ""; 3433 } 3434 return this; 3435 }, 3436 locked: function() { 3437 return !!locked; 3438 }, 3439 3440 // Call all callbacks with the given context and arguments 3441 fireWith: function( context, args ) { 3442 if ( !locked ) { 3443 args = args || []; 3444 args = [ context, args.slice ? args.slice() : args ]; 3445 queue.push( args ); 3446 if ( !firing ) { 3447 fire(); 3448 } 3449 } 3450 return this; 3451 }, 3452 3453 // Call all the callbacks with the given arguments 3454 fire: function() { 3455 self.fireWith( this, arguments ); 3456 return this; 3457 }, 3458 3459 // To know if the callbacks have already been called at least once 3460 fired: function() { 3461 return !!fired; 3462 } 3463 }; 3464 3465 return self; 3466}; 3467 3468 3469function Identity( v ) { 3470 return v; 3471} 3472function Thrower( ex ) { 3473 throw ex; 3474} 3475 3476function adoptValue( value, resolve, reject, noValue ) { 3477 var method; 3478 3479 try { 3480 3481 // Check for promise aspect first to privilege synchronous behavior 3482 if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { 3483 method.call( value ).done( resolve ).fail( reject ); 3484 3485 // Other thenables 3486 } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { 3487 method.call( value, resolve, reject ); 3488 3489 // Other non-thenables 3490 } else { 3491 3492 // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: 3493 // * false: [ value ].slice( 0 ) => resolve( value ) 3494 // * true: [ value ].slice( 1 ) => resolve() 3495 resolve.apply( undefined, [ value ].slice( noValue ) ); 3496 } 3497 3498 // For Promises/A+, convert exceptions into rejections 3499 // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in 3500 // Deferred#then to conditionally suppress rejection. 3501 } catch ( value ) { 3502 3503 // Support: Android 4.0 only 3504 // Strict mode functions invoked without .call/.apply get global-object context 3505 reject.apply( undefined, [ value ]
vendor: 9,153 bytes, lines 3505-3830
3505 ); 3506 } 3507} 3508 3509jQuery.extend( { 3510 3511 Deferred: function( func ) { 3512 var tuples = [ 3513 3514 // action, add listener, callbacks, 3515 // ... .then handlers, argument index, [final state] 3516 [ "notify", "progress", jQuery.Callbacks( "memory" ), 3517 jQuery.Callbacks( "memory" ), 2 ], 3518 [ "resolve", "done", jQuery.Callbacks( "once memory" ), 3519 jQuery.Callbacks( "once memory" ), 0, "resolved" ], 3520 [ "reject", "fail", jQuery.Callbacks( "once memory" ), 3521 jQuery.Callbacks( "once memory" ), 1, "rejected" ] 3522 ], 3523 state = "pending", 3524 promise = { 3525 state: function() { 3526 return state; 3527 }, 3528 always: function() { 3529 deferred.done( arguments ).fail( arguments ); 3530 return this; 3531 }, 3532 "catch": function( fn ) { 3533 return promise.then( null, fn ); 3534 }, 3535 3536 // Keep pipe for back-compat 3537 pipe: function( /* fnDone, fnFail, fnProgress */ ) { 3538 var fns = arguments; 3539 3540 return jQuery.Deferred( function( newDefer ) { 3541 jQuery.each( tuples, function( i, tuple ) { 3542 3543 // Map tuples (progress, done, fail) to arguments (done, fail, progress) 3544 var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; 3545 3546 // deferred.progress(function() { bind to newDefer or newDefer.notify }) 3547 // deferred.done(function() { bind to newDefer or newDefer.resolve }) 3548 // deferred.fail(function() { bind to newDefer or newDefer.reject }) 3549 deferred[ tuple[ 1 ] ]( function() { 3550 var returned = fn && fn.apply( this, arguments ); 3551 if ( returned && jQuery.isFunction( returned.promise ) ) { 3552 returned.promise() 3553 .progress( newDefer.notify ) 3554 .done( newDefer.resolve ) 3555 .fail( newDefer.reject ); 3556 } else { 3557 newDefer[ tuple[ 0 ] + "With" ]( 3558 this, 3559 fn ? [ returned ] : arguments 3560 ); 3561 } 3562 } ); 3563 } ); 3564 fns = null; 3565 } ).promise(); 3566 }, 3567 then: function( onFulfilled, onRejected, onProgress ) { 3568 var maxDepth = 0; 3569 function resolve( depth, deferred, handler, special ) { 3570 return function() { 3571 var that = this, 3572 args = arguments, 3573 mightThrow = function() { 3574 var returned, then; 3575 3576 // Support: Promises/A+ section 2.3.3.3.3 3577 // https://promisesaplus.com/#point-59 3578 // Ignore double-resolution attempts 3579 if ( depth < maxDepth ) { 3580 return; 3581 } 3582 3583 returned = handler.apply( that, args ); 3584 3585 // Support: Promises/A+ section 2.3.1 3586 // https://promisesaplus.com/#point-48 3587 if ( returned === deferred.promise() ) { 3588 throw new TypeError( "Thenable self-resolution" ); 3589 } 3590 3591 // Support: Promises/A+ sections 2.3.3.1, 3.5 3592 // https://promisesaplus.com/#point-54 3593 // https://promisesaplus.com/#point-75 3594 // Retrieve `then` only once 3595 then = returned && 3596 3597 // Support: Promises/A+ section 2.3.4 3598 // https://promisesaplus.com/#point-64 3599 // Only check objects and functions for thenability 3600 ( typeof returned === "object" || 3601 typeof returned === "function" ) && 3602 returned.then; 3603 3604 // Handle a returned thenable 3605 if ( jQuery.isFunction( then ) ) { 3606 3607 // Special processors (notify) just wait for resolution 3608 if ( special ) { 3609 then.call( 3610 returned, 3611 resolve( maxDepth, deferred, Identity, special ), 3612 resolve( maxDepth, deferred, Thrower, special ) 3613 ); 3614 3615 // Normal processors (resolve) also hook into progress 3616 } else { 3617 3618 // ...and disregard older resolution values 3619 maxDepth++; 3620 3621 then.call( 3622 returned, 3623 resolve( maxDepth, deferred, Identity, special ), 3624 resolve( maxDepth, deferred, Thrower, special ), 3625 resolve( maxDepth, deferred, Identity, 3626 deferred.notifyWith ) 3627 ); 3628 } 3629 3630 // Handle all other returned values 3631 } else { 3632 3633 // Only substitute handlers pass on context 3634 // and multiple values (non-spec behavior) 3635 if ( handler !== Identity ) { 3636 that = undefined; 3637 args = [ returned ]; 3638 } 3639 3640 // Process the value(s) 3641 // Default process is resolve 3642 ( special || deferred.resolveWith )( that, args ); 3643 } 3644 }, 3645 3646 // Only normal processors (resolve) catch and reject exceptions 3647 process = special ? 3648 mightThrow : 3649 function() { 3650 try { 3651 mightThrow(); 3652 } catch ( e ) { 3653 3654 if ( jQuery.Deferred.exceptionHook ) { 3655 jQuery.Deferred.exceptionHook( e, 3656 process.stackTrace ); 3657 } 3658 3659 // Support: Promises/A+ section 2.3.3.3.4.1 3660 // https://promisesaplus.com/#point-61 3661 // Ignore post-resolution exceptions 3662 if ( depth + 1 >= maxDepth ) { 3663 3664 // Only substitute handlers pass on context 3665 // and multiple values (non-spec behavior) 3666 if ( handler !== Thrower ) { 3667 that = undefined; 3668 args = [ e ]; 3669 } 3670 3671 deferred.rejectWith( that, args ); 3672 } 3673 } 3674 }; 3675 3676 // Support: Promises/A+ section 2.3.3.3.1 3677 // https://promisesaplus.com/#point-57 3678 // Re-resolve promises immediately to dodge false rejection from 3679 // subsequent errors 3680 if ( depth ) { 3681 process(); 3682 } else { 3683 3684 // Call an optional hook to record the stack, in case of exception 3685 // since it's otherwise lost when execution goes async 3686 if ( jQuery.Deferred.getStackHook ) { 3687 process.stackTrace = jQuery.Deferred.getStackHook(); 3688 } 3689 window.setTimeout( process ); 3690 } 3691 }; 3692 } 3693 3694 return jQuery.Deferred( function( newDefer ) { 3695 3696 // progress_handlers.add( ... ) 3697 tuples[ 0 ][ 3 ].add( 3698 resolve( 3699 0, 3700 newDefer, 3701 jQuery.isFunction( onProgress ) ? 3702 onProgress : 3703 Identity, 3704 newDefer.notifyWith 3705 ) 3706 ); 3707 3708 // fulfilled_handlers.add( ... ) 3709 tuples[ 1 ][ 3 ].add( 3710 resolve( 3711 0, 3712 newDefer, 3713 jQuery.isFunction( onFulfilled ) ? 3714 onFulfilled : 3715 Identity 3716 ) 3717 ); 3718 3719 // rejected_handlers.add( ... ) 3720 tuples[ 2 ][ 3 ].add( 3721 resolve( 3722 0, 3723 newDefer, 3724 jQuery.isFunction( onRejected ) ? 3725 onRejected : 3726 Thrower 3727 ) 3728 ); 3729 } ).promise(); 3730 }, 3731 3732 // Get a promise for this deferred 3733 // If obj is provided, the promise aspect is added to the object 3734 promise: function( obj ) { 3735 return obj != null ? jQuery.extend( obj, promise ) : promise; 3736 } 3737 }, 3738 deferred = {}; 3739 3740 // Add list-specific methods 3741 jQuery.each( tuples, function( i, tuple ) { 3742 var list = tuple[ 2 ], 3743 stateString = tuple[ 5 ]; 3744 3745 // promise.progress = list.add 3746 // promise.done = list.add 3747 // promise.fail = list.add 3748 promise[ tuple[ 1 ] ] = list.add; 3749 3750 // Handle state 3751 if ( stateString ) { 3752 list.add( 3753 function() { 3754 3755 // state = "resolved" (i.e., fulfilled) 3756 // state = "rejected" 3757 state = stateString; 3758 }, 3759 3760 // rejected_callbacks.disable 3761 // fulfilled_callbacks.disable 3762 tuples[ 3 - i ][ 2 ].disable, 3763 3764 // progress_callbacks.lock 3765 tuples[ 0 ][ 2 ].lock 3766 ); 3767 } 3768 3769 // progress_handlers.fire 3770 // fulfilled_handlers.fire 3771 // rejected_handlers.fire 3772 list.add( tuple[ 3 ].fire ); 3773 3774 // deferred.notify = function() { deferred.notifyWith(...) } 3775 // deferred.resolve = function() { deferred.resolveWith(...) } 3776 // deferred.reject = function() { deferred.rejectWith(...) } 3777 deferred[ tuple[ 0 ] ] = function() { 3778 deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); 3779 return this; 3780 }; 3781 3782 // deferred.notifyWith = list.fireWith 3783 // deferred.resolveWith = list.fireWith 3784 // deferred.rejectWith = list.fireWith 3785 deferred[ tuple[ 0 ] + "With" ] = list.fireWith; 3786 } ); 3787 3788 // Make the deferred a promise 3789 promise.promise( deferred ); 3790 3791 // Call given func if any 3792 if ( func ) { 3793 func.call( deferred, deferred ); 3794 } 3795 3796 // All done! 3797 return deferred; 3798 }, 3799 3800 // Deferred helper 3801 when: function( singleValue ) { 3802 var 3803 3804 // count of uncompleted subordinates 3805 remaining = arguments.length, 3806 3807 // count of unprocessed arguments 3808 i = remaining, 3809 3810 // subordinate fulfillment data 3811 resolveContexts = Array( i ), 3812 resolveValues = slice.call( arguments ), 3813 3814 // the master Deferred 3815 master = jQuery.Deferred(), 3816 3817 // subordinate callback factory 3818 updateFunc = function( i ) { 3819 return function( value ) { 3820 resolveContexts[ i ] = this; 3821 resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; 3822 if ( !( --remaining ) ) { 3823 master.resolveWith( resolveContexts, resolveValues ); 3824 } 3825 }; 3826 }; 3827 3828 // Single- and empty arguments are adopted like Promise.resolve 3829 if ( remaining <= 1 ) { 3830 adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject
vendor: 2,079 bytes, lines 3830-3918
3830, 3831 !remaining ); 3832 3833 // Use .then() to unwrap secondary thenables (cf. gh-3000) 3834 if ( master.state() === "pending" || 3835 jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { 3836 3837 return master.then(); 3838 } 3839 } 3840 3841 // Multiple arguments are aggregated like Promise.all array elements 3842 while ( i-- ) { 3843 adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); 3844 } 3845 3846 return master.promise(); 3847 } 3848} ); 3849 3850 3851// These usually indicate a programmer mistake during development, 3852// warn about them ASAP rather than swallowing them by default. 3853var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; 3854 3855jQuery.Deferred.exceptionHook = function( error, stack ) { 3856 3857 // Support: IE 8 - 9 only 3858 // Console exists when dev tools are open, which can happen at any time 3859 if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { 3860 window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); 3861 } 3862}; 3863 3864 3865 3866 3867jQuery.readyException = function( error ) { 3868 window.setTimeout( function() { 3869 throw error; 3870 } ); 3871}; 3872 3873 3874 3875 3876// The deferred used on DOM ready 3877var readyList = jQuery.Deferred(); 3878 3879jQuery.fn.ready = function( fn ) { 3880 3881 readyList 3882 .then( fn ) 3883 3884 // Wrap jQuery.readyException in a function so that the lookup 3885 // happens at the time of error handling instead of callback 3886 // registration. 3887 .catch( function( error ) { 3888 jQuery.readyException( error ); 3889 } ); 3890 3891 return this; 3892}; 3893 3894jQuery.extend( { 3895 3896 // Is the DOM ready to be used? Set to true once it occurs. 3897 isReady: false, 3898 3899 // A counter to track how many items to wait for before 3900 // the ready event fires. See #6781 3901 readyWait: 1, 3902 3903 // Handle when the DOM is ready 3904 ready: function( wait ) { 3905 3906 // Abort if there are pending holds or we're already ready 3907 if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { 3908 return; 3909 } 3910 3911 // Remember that the DOM is ready 3912 jQuery.isReady = true; 3913 3914 // If a normal DOM Ready event fired, decrement, and wait if need be 3915 if ( wait !== true && --jQuery.readyWait > 0 ) { 3916 return; 3917 } 3918
vendor: 4,098 bytes, lines 3919-4100
3919 // If there are functions bound, to execute 3920 readyList.resolveWith( document, [ jQuery ] ); 3921 } 3922} ); 3923 3924jQuery.ready.then = readyList.then; 3925 3926// The ready event handler and self cleanup method 3927function completed() { 3928 document.removeEventListener( "DOMContentLoaded", completed ); 3929 window.removeEventListener( "load", completed ); 3930 jQuery.ready(); 3931} 3932 3933// Catch cases where $(document).ready() is called 3934// after the browser event has already occurred. 3935// Support: IE <=9 - 10 only 3936// Older IE sometimes signals "interactive" too soon 3937if ( document.readyState === "complete" || 3938 ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { 3939 3940 // Handle it asynchronously to allow scripts the opportunity to delay ready 3941 window.setTimeout( jQuery.ready ); 3942 3943} else { 3944 3945 // Use the handy event callback 3946 document.addEventListener( "DOMContentLoaded", completed ); 3947 3948 // A fallback to window.onload, that will always work 3949 window.addEventListener( "load", completed ); 3950} 3951 3952 3953 3954 3955// Multifunctional method to get and set values of a collection 3956// The value/s can optionally be executed if it's a function 3957var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { 3958 var i = 0, 3959 len = elems.length, 3960 bulk = key == null; 3961 3962 // Sets many values 3963 if ( jQuery.type( key ) === "object" ) { 3964 chainable = true; 3965 for ( i in key ) { 3966 access( elems, fn, i, key[ i ], true, emptyGet, raw ); 3967 } 3968 3969 // Sets one value 3970 } else if ( value !== undefined ) { 3971 chainable = true; 3972 3973 if ( !jQuery.isFunction( value ) ) { 3974 raw = true; 3975 } 3976 3977 if ( bulk ) { 3978 3979 // Bulk operations run against the entire set 3980 if ( raw ) { 3981 fn.call( elems, value ); 3982 fn = null; 3983 3984 // ...except when executing function values 3985 } else { 3986 bulk = fn; 3987 fn = function( elem, key, value ) { 3988 return bulk.call( jQuery( elem ), value ); 3989 }; 3990 } 3991 } 3992 3993 if ( fn ) { 3994 for ( ; i < len; i++ ) { 3995 fn( 3996 elems[ i ], key, raw ? 3997 value : 3998 value.call( elems[ i ], i, fn( elems[ i ], key ) ) 3999 ); 4000 } 4001 } 4002 } 4003 4004 if ( chainable ) { 4005 return elems; 4006 } 4007 4008 // Gets 4009 if ( bulk ) { 4010 return fn.call( elems ); 4011 } 4012 4013 return len ? fn( elems[ 0 ], key ) : emptyGet; 4014}; 4015var acceptData = function( owner ) { 4016 4017 // Accepts only: 4018 // - Node 4019 // - Node.ELEMENT_NODE 4020 // - Node.DOCUMENT_NODE 4021 // - Object 4022 // - Any 4023 return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); 4024}; 4025 4026 4027 4028 4029function Data() { 4030 this.expando = jQuery.expando + Data.uid++; 4031} 4032 4033Data.uid = 1; 4034 4035Data.prototype = { 4036 4037 cache: function( owner ) { 4038 4039 // Check if the owner object already has a cache 4040 var value = owner[ this.expando ]; 4041 4042 // If not, create one 4043 if ( !value ) { 4044 value = {}; 4045 4046 // We can accept data for non-element nodes in modern browsers, 4047 // but we should not, see #8335. 4048 // Always return an empty object. 4049 if ( acceptData( owner ) ) { 4050 4051 // If it is a node unlikely to be stringify-ed or looped over 4052 // use plain assignment 4053 if ( owner.nodeType ) { 4054 owner[ this.expando ] = value; 4055 4056 // Otherwise secure it in a non-enumerable property 4057 // configurable must be true to allow the property to be 4058 // deleted when data is removed 4059 } else { 4060 Object.defineProperty( owner, this.expando, { 4061 value: value, 4062 configurable: true 4063 } ); 4064 } 4065 } 4066 } 4067 4068 return value; 4069 }, 4070 set: function( owner, data, value ) { 4071 var prop, 4072 cache = this.cache( owner ); 4073 4074 // Handle: [ owner, key, value ] args 4075 // Always use camelCase key (gh-2257) 4076 if ( typeof data === "string" ) { 4077 cache[ jQuery.camelCase( data ) ] = value; 4078 4079 // Handle: [ owner, { properties } ] args 4080 } else { 4081 4082 // Copy the properties one-by-one to the cache object 4083 for ( prop in data ) { 4084 cache[ jQuery.camelCase( prop ) ] = data[ prop ]; 4085 } 4086 } 4087 return cache; 4088 }, 4089 get: function( owner, key ) { 4090 return key === undefined ? 4091 this.cache( owner ) : 4092 4093 // Always use camelCase key (gh-2257) 4094 owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; 4095 }, 4096 access: function( owner, key, value ) { 4097 4098 // In cases where either: 4099 // 4100 // 1. No key was specified
vendor: 4,593 bytes, lines 4101-4284
4101 // 2. A string key was specified, but no value provided 4102 // 4103 // Take the "read" path and allow the get method to determine 4104 // which value to return, respectively either: 4105 // 4106 // 1. The entire cache object 4107 // 2. The data stored at the key 4108 // 4109 if ( key === undefined || 4110 ( ( key && typeof key === "string" ) && value === undefined ) ) { 4111 4112 return this.get( owner, key ); 4113 } 4114 4115 // When the key is not a string, or both a key and value 4116 // are specified, set or extend (existing objects) with either: 4117 // 4118 // 1. An object of properties 4119 // 2. A key and value 4120 // 4121 this.set( owner, key, value ); 4122 4123 // Since the "set" path can have two possible entry points 4124 // return the expected data based on which path was taken[*] 4125 return value !== undefined ? value : key; 4126 }, 4127 remove: function( owner, key ) { 4128 var i, 4129 cache = owner[ this.expando ]; 4130 4131 if ( cache === undefined ) { 4132 return; 4133 } 4134 4135 if ( key !== undefined ) { 4136 4137 // Support array or space separated string of keys 4138 if ( Array.isArray( key ) ) { 4139 4140 // If key is an array of keys... 4141 // We always set camelCase keys, so remove that. 4142 key = key.map( jQuery.camelCase ); 4143 } else { 4144 key = jQuery.camelCase( key ); 4145 4146 // If a key with the spaces exists, use it. 4147 // Otherwise, create an array by matching non-whitespace 4148 key = key in cache ? 4149 [ key ] : 4150 ( key.match( rnothtmlwhite ) || [] ); 4151 } 4152 4153 i = key.length; 4154 4155 while ( i-- ) { 4156 delete cache[ key[ i ] ]; 4157 } 4158 } 4159 4160 // Remove the expando if there's no more data 4161 if ( key === undefined || jQuery.isEmptyObject( cache ) ) { 4162 4163 // Support: Chrome <=35 - 45 4164 // Webkit & Blink performance suffers when deleting properties 4165 // from DOM nodes, so set to undefined instead 4166 // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) 4167 if ( owner.nodeType ) { 4168 owner[ this.expando ] = undefined; 4169 } else { 4170 delete owner[ this.expando ]; 4171 } 4172 } 4173 }, 4174 hasData: function( owner ) { 4175 var cache = owner[ this.expando ]; 4176 return cache !== undefined && !jQuery.isEmptyObject( cache ); 4177 } 4178}; 4179var dataPriv = new Data(); 4180 4181var dataUser = new Data(); 4182 4183 4184 4185// Implementation Summary 4186// 4187// 1. Enforce API surface and semantic compatibility with 1.9.x branch 4188// 2. Improve the module's maintainability by reducing the storage 4189// paths to a single mechanism. 4190// 3. Use the same single mechanism to support "private" and "user" data. 4191// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) 4192// 5. Avoid exposing implementation details on user objects (eg. expando properties) 4193// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 4194 4195var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, 4196 rmultiDash = /[A-Z]/g; 4197 4198function getData( data ) { 4199 if ( data === "true" ) { 4200 return true; 4201 } 4202 4203 if ( data === "false" ) { 4204 return false; 4205 } 4206 4207 if ( data === "null" ) { 4208 return null; 4209 } 4210 4211 // Only convert to a number if it doesn't change the string 4212 if ( data === +data + "" ) { 4213 return +data; 4214 } 4215 4216 if ( rbrace.test( data ) ) { 4217 return JSON.parse( data ); 4218 } 4219 4220 return data; 4221} 4222 4223function dataAttr( elem, key, data ) { 4224 var name; 4225 4226 // If nothing was found internally, try to fetch any 4227 // data from the HTML5 data-* attribute 4228 if ( data === undefined && elem.nodeType === 1 ) { 4229 name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); 4230 data = elem.getAttribute( name ); 4231 4232 if ( typeof data === "string" ) { 4233 try { 4234 data = getData( data ); 4235 } catch ( e ) {} 4236 4237 // Make sure we set the data so it isn't changed later 4238 dataUser.set( elem, key, data ); 4239 } else { 4240 data = undefined; 4241 } 4242 } 4243 return data; 4244} 4245 4246jQuery.extend( { 4247 hasData: function( elem ) { 4248 return dataUser.hasData( elem ) || dataPriv.hasData( elem ); 4249 }, 4250 4251 data: function( elem, name, data ) { 4252 return dataUser.access( elem, name, data ); 4253 }, 4254 4255 removeData: function( elem, name ) { 4256 dataUser.remove( elem, name ); 4257 }, 4258 4259 // TODO: Now that all calls to _data and _removeData have been replaced 4260 // with direct calls to dataPriv methods, these can be deprecated. 4261 _data: function( elem, name, data ) { 4262 return dataPriv.access( elem, name, data ); 4263 }, 4264 4265 _removeData: function( elem, name ) { 4266 dataPriv.remove( elem, name ); 4267 } 4268} ); 4269 4270jQuery.fn.extend( { 4271 data: function( key, value ) { 4272 var i, name, data, 4273 elem = this[ 0 ], 4274 attrs = elem && elem.attributes; 4275 4276 // Gets all values 4277 if ( key === undefined ) { 4278 if ( this.length ) { 4279 data = dataUser.get( elem ); 4280 4281 if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { 4282 i = attrs.length; 4283 while ( i-- ) { 4284
vendor: 7,789 bytes, lines 4285-4607
4285 // Support: IE 11 only 4286 // The attrs elements can be null (#14894) 4287 if ( attrs[ i ] ) { 4288 name = attrs[ i ].name; 4289 if ( name.indexOf( "data-" ) === 0 ) { 4290 name = jQuery.camelCase( name.slice( 5 ) ); 4291 dataAttr( elem, name, data[ name ] ); 4292 } 4293 } 4294 } 4295 dataPriv.set( elem, "hasDataAttrs", true ); 4296 } 4297 } 4298 4299 return data; 4300 } 4301 4302 // Sets multiple values 4303 if ( typeof key === "object" ) { 4304 return this.each( function() { 4305 dataUser.set( this, key ); 4306 } ); 4307 } 4308 4309 return access( this, function( value ) { 4310 var data; 4311 4312 // The calling jQuery object (element matches) is not empty 4313 // (and therefore has an element appears at this[ 0 ]) and the 4314 // `value` parameter was not undefined. An empty jQuery object 4315 // will result in `undefined` for elem = this[ 0 ] which will 4316 // throw an exception if an attempt to read a data cache is made. 4317 if ( elem && value === undefined ) { 4318 4319 // Attempt to get data from the cache 4320 // The key will always be camelCased in Data 4321 data = dataUser.get( elem, key ); 4322 if ( data !== undefined ) { 4323 return data; 4324 } 4325 4326 // Attempt to "discover" the data in 4327 // HTML5 custom data-* attrs 4328 data = dataAttr( elem, key ); 4329 if ( data !== undefined ) { 4330 return data; 4331 } 4332 4333 // We tried really hard, but the data doesn't exist. 4334 return; 4335 } 4336 4337 // Set the data... 4338 this.each( function() { 4339 4340 // We always store the camelCased key 4341 dataUser.set( this, key, value ); 4342 } ); 4343 }, null, value, arguments.length > 1, null, true ); 4344 }, 4345 4346 removeData: function( key ) { 4347 return this.each( function() { 4348 dataUser.remove( this, key ); 4349 } ); 4350 } 4351} ); 4352 4353 4354jQuery.extend( { 4355 queue: function( elem, type, data ) { 4356 var queue; 4357 4358 if ( elem ) { 4359 type = ( type || "fx" ) + "queue"; 4360 queue = dataPriv.get( elem, type ); 4361 4362 // Speed up dequeue by getting out quickly if this is just a lookup 4363 if ( data ) { 4364 if ( !queue || Array.isArray( data ) ) { 4365 queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); 4366 } else { 4367 queue.push( data ); 4368 } 4369 } 4370 return queue || []; 4371 } 4372 }, 4373 4374 dequeue: function( elem, type ) { 4375 type = type || "fx"; 4376 4377 var queue = jQuery.queue( elem, type ), 4378 startLength = queue.length, 4379 fn = queue.shift(), 4380 hooks = jQuery._queueHooks( elem, type ), 4381 next = function() { 4382 jQuery.dequeue( elem, type ); 4383 }; 4384 4385 // If the fx queue is dequeued, always remove the progress sentinel 4386 if ( fn === "inprogress" ) { 4387 fn = queue.shift(); 4388 startLength--; 4389 } 4390 4391 if ( fn ) { 4392 4393 // Add a progress sentinel to prevent the fx queue from being 4394 // automatically dequeued 4395 if ( type === "fx" ) { 4396 queue.unshift( "inprogress" ); 4397 } 4398 4399 // Clear up the last queue stop function 4400 delete hooks.stop; 4401 fn.call( elem, next, hooks ); 4402 } 4403 4404 if ( !startLength && hooks ) { 4405 hooks.empty.fire(); 4406 } 4407 }, 4408 4409 // Not public - generate a queueHooks object, or return the current one 4410 _queueHooks: function( elem, type ) { 4411 var key = type + "queueHooks"; 4412 return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { 4413 empty: jQuery.Callbacks( "once memory" ).add( function() { 4414 dataPriv.remove( elem, [ type + "queue", key ] ); 4415 } ) 4416 } ); 4417 } 4418} ); 4419 4420jQuery.fn.extend( { 4421 queue: function( type, data ) { 4422 var setter = 2; 4423 4424 if ( typeof type !== "string" ) { 4425 data = type; 4426 type = "fx"; 4427 setter--; 4428 } 4429 4430 if ( arguments.length < setter ) { 4431 return jQuery.queue( this[ 0 ], type ); 4432 } 4433 4434 return data === undefined ? 4435 this : 4436 this.each( function() { 4437 var queue = jQuery.queue( this, type, data ); 4438 4439 // Ensure a hooks for this queue 4440 jQuery._queueHooks( this, type ); 4441 4442 if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { 4443 jQuery.dequeue( this, type ); 4444 } 4445 } ); 4446 }, 4447 dequeue: function( type ) { 4448 return this.each( function() { 4449 jQuery.dequeue( this, type ); 4450 } ); 4451 }, 4452 clearQueue: function( type ) { 4453 return this.queue( type || "fx", [] ); 4454 }, 4455 4456 // Get a promise resolved when queues of a certain type 4457 // are emptied (fx is the type by default) 4458 promise: function( type, obj ) { 4459 var tmp, 4460 count = 1, 4461 defer = jQuery.Deferred(), 4462 elements = this, 4463 i = this.length, 4464 resolve = function() { 4465 if ( !( --count ) ) { 4466 defer.resolveWith( elements, [ elements ] ); 4467 } 4468 }; 4469 4470 if ( typeof type !== "string" ) { 4471 obj = type; 4472 type = undefined; 4473 } 4474 type = type || "fx"; 4475 4476 while ( i-- ) { 4477 tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); 4478 if ( tmp && tmp.empty ) { 4479 count++; 4480 tmp.empty.add( resolve ); 4481 } 4482 } 4483 resolve(); 4484 return defer.promise( obj ); 4485 } 4486} ); 4487var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; 4488 4489var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); 4490 4491 4492var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; 4493 4494var isHiddenWithinTree = function( elem, el ) { 4495 4496 // isHiddenWithinTree might be called from jQuery#filter function; 4497 // in that case, element will be second argument 4498 elem = el || elem; 4499 4500 // Inline style trumps all 4501 return elem.style.display === "none" || 4502 elem.style.display === "" && 4503 4504 // Otherwise, check computed style 4505 // Support: Firefox <=43 - 45 4506 // Disconnected elements can have computed display: none, so first confirm that elem is 4507 // in the document. 4508 jQuery.contains( elem.ownerDocument, elem ) && 4509 4510 jQuery.css( elem, "display" ) === "none"; 4511 }; 4512 4513var swap = function( elem, options, callback, args ) { 4514 var ret, name, 4515 old = {}; 4516 4517 // Remember the old values, and insert the new ones 4518 for ( name in options ) { 4519 old[ name ] = elem.style[ name ]; 4520 elem.style[ name ] = options[ name ]; 4521 } 4522 4523 ret = callback.apply( elem, args || [] ); 4524 4525 // Revert the old values 4526 for ( name in options ) { 4527 elem.style[ name ] = old[ name ]; 4528 } 4529 4530 return ret; 4531}; 4532 4533 4534 4535 4536function adjustCSS( elem, prop, valueParts, tween ) { 4537 var adjusted, 4538 scale = 1, 4539 maxIterations = 20, 4540 currentValue = tween ? 4541 function() { 4542 return tween.cur(); 4543 } : 4544 function() { 4545 return jQuery.css( elem, prop, "" ); 4546 }, 4547 initial = currentValue(), 4548 unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), 4549 4550 // Starting value computation is required for potential unit mismatches 4551 initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && 4552 rcssNum.exec( jQuery.css( elem, prop ) ); 4553 4554 if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { 4555 4556 // Trust units reported by jQuery.css 4557 unit = unit || initialInUnit[ 3 ]; 4558 4559 // Make sure we update the tween properties later on 4560 valueParts = valueParts || []; 4561 4562 // Iteratively approximate from a nonzero starting point 4563 initialInUnit = +initial || 1; 4564 4565 do { 4566 4567 // If previous iteration zeroed out, double until we get *something*. 4568 // Use string for doubling so we don't accidentally see scale as unchanged below 4569 scale = scale || ".5"; 4570 4571 // Adjust and apply 4572 initialInUnit = initialInUnit / scale; 4573 jQuery.style( elem, prop, initialInUnit + unit ); 4574 4575 // Update scale, tolerating zero or NaN from tween.cur() 4576 // Break the loop if scale is unchanged or perfect, or if we've just had enough. 4577 } while ( 4578 scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations 4579 ); 4580 } 4581 4582 if ( valueParts ) { 4583 initialInUnit = +initialInUnit || +initial || 0; 4584 4585 // Apply relative offset (+=/-=) if specified 4586 adjusted = valueParts[ 1 ] ? 4587 initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : 4588 +valueParts[ 2 ]; 4589 if ( tween ) { 4590 tween.unit = unit; 4591 tween.start = initialInUnit; 4592 tween.end = adjusted; 4593 } 4594 } 4595 return adjusted; 4596} 4597 4598 4599var defaultDisplayMap = {}; 4600 4601function getDefaultDisplay( elem ) { 4602 var temp, 4603 doc = elem.ownerDocument, 4604 nodeName = elem.nodeName, 4605 display = defaultDisplayMap[ nodeName ]; 4606 4607 if ( display ) {
vendor: 4,001 bytes, lines 4608-4779
4608 return display; 4609 } 4610 4611 temp = doc.body.appendChild( doc.createElement( nodeName ) ); 4612 display = jQuery.css( temp, "display" ); 4613 4614 temp.parentNode.removeChild( temp ); 4615 4616 if ( display === "none" ) { 4617 display = "block"; 4618 } 4619 defaultDisplayMap[ nodeName ] = display; 4620 4621 return display; 4622} 4623 4624function showHide( elements, show ) { 4625 var display, elem, 4626 values = [], 4627 index = 0, 4628 length = elements.length; 4629 4630 // Determine new display value for elements that need to change 4631 for ( ; index < length; index++ ) { 4632 elem = elements[ index ]; 4633 if ( !elem.style ) { 4634 continue; 4635 } 4636 4637 display = elem.style.display; 4638 if ( show ) { 4639 4640 // Since we force visibility upon cascade-hidden elements, an immediate (and slow) 4641 // check is required in this first loop unless we have a nonempty display value (either 4642 // inline or about-to-be-restored) 4643 if ( display === "none" ) { 4644 values[ index ] = dataPriv.get( elem, "display" ) || null; 4645 if ( !values[ index ] ) { 4646 elem.style.display = ""; 4647 } 4648 } 4649 if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { 4650 values[ index ] = getDefaultDisplay( elem ); 4651 } 4652 } else { 4653 if ( display !== "none" ) { 4654 values[ index ] = "none"; 4655 4656 // Remember what we're overwriting 4657 dataPriv.set( elem, "display", display ); 4658 } 4659 } 4660 } 4661 4662 // Set the display of the elements in a second loop to avoid constant reflow 4663 for ( index = 0; index < length; index++ ) { 4664 if ( values[ index ] != null ) { 4665 elements[ index ].style.display = values[ index ]; 4666 } 4667 } 4668 4669 return elements; 4670} 4671 4672jQuery.fn.extend( { 4673 show: function() { 4674 return showHide( this, true ); 4675 }, 4676 hide: function() { 4677 return showHide( this ); 4678 }, 4679 toggle: function( state ) { 4680 if ( typeof state === "boolean" ) { 4681 return state ? this.show() : this.hide(); 4682 } 4683 4684 return this.each( function() { 4685 if ( isHiddenWithinTree( this ) ) { 4686 jQuery( this ).show(); 4687 } else { 4688 jQuery( this ).hide(); 4689 } 4690 } ); 4691 } 4692} ); 4693var rcheckableType = ( /^(?:checkbox|radio)$/i ); 4694 4695var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); 4696 4697var rscriptType = ( /^$|\/(?:java|ecma)script/i ); 4698 4699 4700 4701// We have to close these tags to support XHTML (#13200) 4702var wrapMap = { 4703 4704 // Support: IE <=9 only 4705 option: [ 1, "<select multiple='multiple'>", "</select>" ], 4706 4707 // XHTML parsers do not magically insert elements in the 4708 // same way that tag soup parsers do. So we cannot shorten 4709 // this by omitting <tbody> or other required elements. 4710 thead: [ 1, "<table>", "</table>" ], 4711 col: [ 2, "<table><colgroup>", "</colgroup></table>" ], 4712 tr: [ 2, "<table><tbody>", "</tbody></table>" ], 4713 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], 4714 4715 _default: [ 0, "", "" ] 4716}; 4717 4718// Support: IE <=9 only 4719wrapMap.optgroup = wrapMap.option; 4720 4721wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; 4722wrapMap.th = wrapMap.td; 4723 4724 4725function getAll( context, tag ) { 4726 4727 // Support: IE <=9 - 11 only 4728 // Use typeof to avoid zero-argument method invocation on host objects (#15151) 4729 var ret; 4730 4731 if ( typeof context.getElementsByTagName !== "undefined" ) { 4732 ret = context.getElementsByTagName( tag || "*" ); 4733 4734 } else if ( typeof context.querySelectorAll !== "undefined" ) { 4735 ret = context.querySelectorAll( tag || "*" ); 4736 4737 } else { 4738 ret = []; 4739 } 4740 4741 if ( tag === undefined || tag && nodeName( context, tag ) ) { 4742 return jQuery.merge( [ context ], ret ); 4743 } 4744 4745 return ret; 4746} 4747 4748 4749// Mark scripts as having already been evaluated 4750function setGlobalEval( elems, refElements ) { 4751 var i = 0, 4752 l = elems.length; 4753 4754 for ( ; i < l; i++ ) { 4755 dataPriv.set( 4756 elems[ i ], 4757 "globalEval", 4758 !refElements || dataPriv.get( refElements[ i ], "globalEval" ) 4759 ); 4760 } 4761} 4762 4763 4764var rhtml = /<|&#?\w+;/; 4765 4766function buildFragment( elems, context, scripts, selection, ignored ) { 4767 var elem, tmp, tag, wrap, contains, j, 4768 fragment = context.createDocumentFragment(), 4769 nodes = [], 4770 i = 0, 4771 l = elems.length; 4772 4773 for ( ; i < l; i++ ) { 4774 elem = elems[ i ]; 4775 4776 if ( elem || elem === 0 ) { 4777 4778 // Add nodes directly 4779 if ( jQuery.t
vendor: 14,415 bytes, lines 4779-5289
4779ype( elem ) === "object" ) { 4780 4781 // Support: Android <=4.0 only, PhantomJS 1 only 4782 // push.apply(_, arraylike) throws on ancient WebKit 4783 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); 4784 4785 // Convert non-html into a text node 4786 } else if ( !rhtml.test( elem ) ) { 4787 nodes.push( context.createTextNode( elem ) ); 4788 4789 // Convert html into DOM nodes 4790 } else { 4791 tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); 4792 4793 // Deserialize a standard representation 4794 tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); 4795 wrap = wrapMap[ tag ] || wrapMap._default; 4796 tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; 4797 4798 // Descend through wrappers to the right content 4799 j = wrap[ 0 ]; 4800 while ( j-- ) { 4801 tmp = tmp.lastChild; 4802 } 4803 4804 // Support: Android <=4.0 only, PhantomJS 1 only 4805 // push.apply(_, arraylike) throws on ancient WebKit 4806 jQuery.merge( nodes, tmp.childNodes ); 4807 4808 // Remember the top-level container 4809 tmp = fragment.firstChild; 4810 4811 // Ensure the created nodes are orphaned (#12392) 4812 tmp.textContent = ""; 4813 } 4814 } 4815 } 4816 4817 // Remove wrapper from fragment 4818 fragment.textContent = ""; 4819 4820 i = 0; 4821 while ( ( elem = nodes[ i++ ] ) ) { 4822 4823 // Skip elements already in the context collection (trac-4087) 4824 if ( selection && jQuery.inArray( elem, selection ) > -1 ) { 4825 if ( ignored ) { 4826 ignored.push( elem ); 4827 } 4828 continue; 4829 } 4830 4831 contains = jQuery.contains( elem.ownerDocument, elem ); 4832 4833 // Append to fragment 4834 tmp = getAll( fragment.appendChild( elem ), "script" ); 4835 4836 // Preserve script evaluation history 4837 if ( contains ) { 4838 setGlobalEval( tmp ); 4839 } 4840 4841 // Capture executables 4842 if ( scripts ) { 4843 j = 0; 4844 while ( ( elem = tmp[ j++ ] ) ) { 4845 if ( rscriptType.test( elem.type || "" ) ) { 4846 scripts.push( elem ); 4847 } 4848 } 4849 } 4850 } 4851 4852 return fragment; 4853} 4854 4855 4856( function() { 4857 var fragment = document.createDocumentFragment(), 4858 div = fragment.appendChild( document.createElement( "div" ) ), 4859 input = document.createElement( "input" ); 4860 4861 // Support: Android 4.0 - 4.3 only 4862 // Check state lost if the name is set (#11217) 4863 // Support: Windows Web Apps (WWA) 4864 // `name` and `type` must use .setAttribute for WWA (#14901) 4865 input.setAttribute( "type", "radio" ); 4866 input.setAttribute( "checked", "checked" ); 4867 input.setAttribute( "name", "t" ); 4868 4869 div.appendChild( input ); 4870 4871 // Support: Android <=4.1 only 4872 // Older WebKit doesn't clone checked state correctly in fragments 4873 support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; 4874 4875 // Support: IE <=11 only 4876 // Make sure textarea (and checkbox) defaultValue is properly cloned 4877 div.innerHTML = "<textarea>x</textarea>"; 4878 support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; 4879} )(); 4880var documentElement = document.documentElement; 4881 4882 4883 4884var 4885 rkeyEvent = /^key/, 4886 rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, 4887 rtypenamespace = /^([^.]*)(?:\.(.+)|)/; 4888 4889function returnTrue() { 4890 return true; 4891} 4892 4893function returnFalse() { 4894 return false; 4895} 4896 4897// Support: IE <=9 only 4898// See #13393 for more info 4899function safeActiveElement() { 4900 try { 4901 return document.activeElement; 4902 } catch ( err ) { } 4903} 4904 4905function on( elem, types, selector, data, fn, one ) { 4906 var origFn, type; 4907 4908 // Types can be a map of types/handlers 4909 if ( typeof types === "object" ) { 4910 4911 // ( types-Object, selector, data ) 4912 if ( typeof selector !== "string" ) { 4913 4914 // ( types-Object, data ) 4915 data = data || selector; 4916 selector = undefined; 4917 } 4918 for ( type in types ) { 4919 on( elem, type, selector, data, types[ type ], one ); 4920 } 4921 return elem; 4922 } 4923 4924 if ( data == null && fn == null ) { 4925 4926 // ( types, fn ) 4927 fn = selector; 4928 data = selector = undefined; 4929 } else if ( fn == null ) { 4930 if ( typeof selector === "string" ) { 4931 4932 // ( types, selector, fn ) 4933 fn = data; 4934 data = undefined; 4935 } else { 4936 4937 // ( types, data, fn ) 4938 fn = data; 4939 data = selector; 4940 selector = undefined; 4941 } 4942 } 4943 if ( fn === false ) { 4944 fn = returnFalse; 4945 } else if ( !fn ) { 4946 return elem; 4947 } 4948 4949 if ( one === 1 ) { 4950 origFn = fn; 4951 fn = function( event ) { 4952 4953 // Can use an empty set, since event contains the info 4954 jQuery().off( event ); 4955 return origFn.apply( this, arguments ); 4956 }; 4957 4958 // Use same guid so caller can remove using origFn 4959 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); 4960 } 4961 return elem.each( function() { 4962 jQuery.event.add( this, types, fn, data, selector ); 4963 } ); 4964} 4965 4966/* 4967 * Helper functions for managing events -- not part of the public interface. 4968 * Props to Dean Edwards' addEvent library for many of the ideas. 4969 */ 4970jQuery.event = { 4971 4972 global: {}, 4973 4974 add: function( elem, types, handler, data, selector ) { 4975 4976 var handleObjIn, eventHandle, tmp, 4977 events, t, handleObj, 4978 special, handlers, type, namespaces, origType, 4979 elemData = dataPriv.get( elem ); 4980 4981 // Don't attach events to noData or text/comment nodes (but allow plain objects) 4982 if ( !elemData ) { 4983 return; 4984 } 4985 4986 // Caller can pass in an object of custom data in lieu of the handler 4987 if ( handler.handler ) { 4988 handleObjIn = handler; 4989 handler = handleObjIn.handler; 4990 selector = handleObjIn.selector; 4991 } 4992 4993 // Ensure that invalid selectors throw exceptions at attach time 4994 // Evaluate against documentElement in case elem is a non-element node (e.g., document) 4995 if ( selector ) { 4996 jQuery.find.matchesSelector( documentElement, selector ); 4997 } 4998 4999 // Make sure that the handler has a unique ID, used to find/remove it later 5000 if ( !handler.guid ) { 5001 handler.guid = jQuery.guid++; 5002 } 5003 5004 // Init the element's event structure and main handler, if this is the first 5005 if ( !( events = elemData.events ) ) { 5006 events = elemData.events = {}; 5007 } 5008 if ( !( eventHandle = elemData.handle ) ) { 5009 eventHandle = elemData.handle = function( e ) { 5010 5011 // Discard the second event of a jQuery.event.trigger() and 5012 // when an event is called after a page has unloaded 5013 return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? 5014 jQuery.event.dispatch.apply( elem, arguments ) : undefined; 5015 }; 5016 } 5017 5018 // Handle multiple events separated by a space 5019 types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; 5020 t = types.length; 5021 while ( t-- ) { 5022 tmp = rtypenamespace.exec( types[ t ] ) || []; 5023 type = origType = tmp[ 1 ]; 5024 namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); 5025 5026 // There *must* be a type, no attaching namespace-only handlers 5027 if ( !type ) { 5028 continue; 5029 } 5030 5031 // If event changes its type, use the special event handlers for the changed type 5032 special = jQuery.event.special[ type ] || {}; 5033 5034 // If selector defined, determine special event api type, otherwise given type 5035 type = ( selector ? special.delegateType : special.bindType ) || type; 5036 5037 // Update special based on newly reset type 5038 special = jQuery.event.special[ type ] || {}; 5039 5040 // handleObj is passed to all event handlers 5041 handleObj = jQuery.extend( { 5042 type: type, 5043 origType: origType, 5044 data: data, 5045 handler: handler, 5046 guid: handler.guid, 5047 selector: selector, 5048 needsContext: selector && jQuery.expr.match.needsContext.test( selector ), 5049 namespace: namespaces.join( "." ) 5050 }, handleObjIn ); 5051 5052 // Init the event handler queue if we're the first 5053 if ( !( handlers = events[ type ] ) ) { 5054 handlers = events[ type ] = []; 5055 handlers.delegateCount = 0; 5056 5057 // Only use addEventListener if the special events handler returns false 5058 if ( !special.setup || 5059 special.setup.call( elem, data, namespaces, eventHandle ) === false ) { 5060 5061 if ( elem.addEventListener ) { 5062 elem.addEventListener( type, eventHandle ); 5063 } 5064 } 5065 } 5066 5067 if ( special.add ) { 5068 special.add.call( elem, handleObj ); 5069 5070 if ( !handleObj.handler.guid ) { 5071 handleObj.handler.guid = handler.guid; 5072 } 5073 } 5074 5075 // Add to the element's handler list, delegates in front 5076 if ( selector ) { 5077 handlers.splice( handlers.delegateCount++, 0, handleObj ); 5078 } else { 5079 handlers.push( handleObj ); 5080 } 5081 5082 // Keep track of which events have ever been used, for event optimization 5083 jQuery.event.global[ type ] = true; 5084 } 5085 5086 }, 5087 5088 // Detach an event or set of events from an element 5089 remove: function( elem, types, handler, selector, mappedTypes ) { 5090 5091 var j, origCount, tmp, 5092 events, t, handleObj, 5093 special, handlers, type, namespaces, origType, 5094 elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); 5095 5096 if ( !elemData || !( events = elemData.events ) ) { 5097 return; 5098 } 5099 5100 // Once for each type.namespace in types; type may be omitted 5101 types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; 5102 t = types.length; 5103 while ( t-- ) { 5104 tmp = rtypenamespace.exec( types[ t ] ) || []; 5105 type = origType = tmp[ 1 ]; 5106 namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); 5107 5108 // Unbind all events (on this namespace, if provided) for the element 5109 if ( !type ) { 5110 for ( type in events ) { 5111 jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); 5112 } 5113 continue; 5114 } 5115 5116 special = jQuery.event.special[ type ] || {}; 5117 type = ( selector ? special.delegateType : special.bindType ) || type; 5118 handlers = events[ type ] || []; 5119 tmp = tmp[ 2 ] && 5120 new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); 5121 5122 // Remove matching events 5123 origCount = j = handlers.length; 5124 while ( j-- ) { 5125 handleObj = handlers[ j ]; 5126 5127 if ( ( mappedTypes || origType === handleObj.origType ) && 5128 ( !handler || handler.guid === handleObj.guid ) && 5129 ( !tmp || tmp.test( handleObj.namespace ) ) && 5130 ( !selector || selector === handleObj.selector || 5131 selector === "**" && handleObj.selector ) ) { 5132 handlers.splice( j, 1 ); 5133 5134 if ( handleObj.selector ) { 5135 handlers.delegateCount--; 5136 } 5137 if ( special.remove ) { 5138 special.remove.call( elem, handleObj ); 5139 } 5140 } 5141 } 5142 5143 // Remove generic event handler if we removed something and no more handlers exist 5144 // (avoids potential for endless recursion during removal of special event handlers) 5145 if ( origCount && !handlers.length ) { 5146 if ( !special.teardown || 5147 special.teardown.call( elem, namespaces, elemData.handle ) === false ) { 5148 5149 jQuery.removeEvent( elem, type, elemData.handle ); 5150 } 5151 5152 delete events[ type ]; 5153 } 5154 } 5155 5156 // Remove data and the expando if it's no longer used 5157 if ( jQuery.isEmptyObject( events ) ) { 5158 dataPriv.remove( elem, "handle events" ); 5159 } 5160 }, 5161 5162 dispatch: function( nativeEvent ) { 5163 5164 // Make a writable jQuery.Event from the native event object 5165 var event = jQuery.event.fix( nativeEvent ); 5166 5167 var i, j, ret, matched, handleObj, handlerQueue, 5168 args = new Array( arguments.length ), 5169 handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], 5170 special = jQuery.event.special[ event.type ] || {}; 5171 5172 // Use the fix-ed jQuery.Event rather than the (read-only) native event 5173 args[ 0 ] = event; 5174 5175 for ( i = 1; i < arguments.length; i++ ) { 5176 args[ i ] = arguments[ i ]; 5177 } 5178 5179 event.delegateTarget = this; 5180 5181 // Call the preDispatch hook for the mapped type, and let it bail if desired 5182 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { 5183 return; 5184 } 5185 5186 // Determine handlers 5187 handlerQueue = jQuery.event.handlers.call( this, event, handlers ); 5188 5189 // Run delegates first; they may want to stop propagation beneath us 5190 i = 0; 5191 while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { 5192 event.currentTarget = matched.elem; 5193 5194 j = 0; 5195 while ( ( handleObj = matched.handlers[ j++ ] ) && 5196 !event.isImmediatePropagationStopped() ) { 5197 5198 // Triggered event must either 1) have no namespace, or 2) have namespace(s) 5199 // a subset or equal to those in the bound event (both can have no namespace). 5200 if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { 5201 5202 event.handleObj = handleObj; 5203 event.data = handleObj.data; 5204 5205 ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || 5206 handleObj.handler ).apply( matched.elem, args ); 5207 5208 if ( ret !== undefined ) { 5209 if ( ( event.result = ret ) === false ) { 5210 event.preventDefault(); 5211 event.stopPropagation(); 5212 } 5213 } 5214 } 5215 } 5216 } 5217 5218 // Call the postDispatch hook for the mapped type 5219 if ( special.postDispatch ) { 5220 special.postDispatch.call( this, event ); 5221 } 5222 5223 return event.result; 5224 }, 5225 5226 handlers: function( event, handlers ) { 5227 var i, handleObj, sel, matchedHandlers, matchedSelectors, 5228 handlerQueue = [], 5229 delegateCount = handlers.delegateCount, 5230 cur = event.target; 5231 5232 // Find delegate handlers 5233 if ( delegateCount && 5234 5235 // Support: IE <=9 5236 // Black-hole SVG <use> instance trees (trac-13180) 5237 cur.nodeType && 5238 5239 // Support: Firefox <=42 5240 // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) 5241 // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click 5242 // Support: IE 11 only 5243 // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) 5244 !( event.type === "click" && event.button >= 1 ) ) { 5245 5246 for ( ; cur !== this; cur = cur.parentNode || this ) { 5247 5248 // Don't check non-elements (#13208) 5249 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) 5250 if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { 5251 matchedHandlers = []; 5252 matchedSelectors = {}; 5253 for ( i = 0; i < delegateCount; i++ ) { 5254 handleObj = handlers[ i ]; 5255 5256 // Don't conflict with Object.prototype properties (#13203) 5257 sel = handleObj.selector + " "; 5258 5259 if ( matchedSelectors[ sel ] === undefined ) { 5260 matchedSelectors[ sel ] = handleObj.needsContext ? 5261 jQuery( sel, this ).index( cur ) > -1 : 5262 jQuery.find( sel, this, null, [ cur ] ).length; 5263 } 5264 if ( matchedSelectors[ sel ] ) { 5265 matchedHandlers.push( handleObj ); 5266 } 5267 } 5268 if ( matchedHandlers.length ) { 5269 handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); 5270 } 5271 } 5272 } 5273 } 5274 5275 // Add the remaining (directly-bound) handlers 5276 cur = this; 5277 if ( delegateCount < handlers.length ) { 5278 handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); 5279 } 5280 5281 return handlerQueue; 5282 }, 5283 5284 addProp: function( name, hook ) { 5285 Object.defineProperty( jQuery.Event.prototype, name, { 5286 enumerable: true, 5287 configurable: true, 5288 5289 get:
vendor: 8,400 bytes, lines 5289-5638
5289jQuery.isFunction( hook ) ? 5290 function() { 5291 if ( this.originalEvent ) { 5292 return hook( this.originalEvent ); 5293 } 5294 } : 5295 function() { 5296 if ( this.originalEvent ) { 5297 return this.originalEvent[ name ]; 5298 } 5299 }, 5300 5301 set: function( value ) { 5302 Object.defineProperty( this, name, { 5303 enumerable: true, 5304 configurable: true, 5305 writable: true, 5306 value: value 5307 } ); 5308 } 5309 } ); 5310 }, 5311 5312 fix: function( originalEvent ) { 5313 return originalEvent[ jQuery.expando ] ? 5314 originalEvent : 5315 new jQuery.Event( originalEvent ); 5316 }, 5317 5318 special: { 5319 load: { 5320 5321 // Prevent triggered image.load events from bubbling to window.load 5322 noBubble: true 5323 }, 5324 focus: { 5325 5326 // Fire native event if possible so blur/focus sequence is correct 5327 trigger: function() { 5328 if ( this !== safeActiveElement() && this.focus ) { 5329 this.focus(); 5330 return false; 5331 } 5332 }, 5333 delegateType: "focusin" 5334 }, 5335 blur: { 5336 trigger: function() { 5337 if ( this === safeActiveElement() && this.blur ) { 5338 this.blur(); 5339 return false; 5340 } 5341 }, 5342 delegateType: "focusout" 5343 }, 5344 click: { 5345 5346 // For checkbox, fire native event so checked state will be right 5347 trigger: function() { 5348 if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { 5349 this.click(); 5350 return false; 5351 } 5352 }, 5353 5354 // For cross-browser consistency, don't fire native .click() on links 5355 _default: function( event ) { 5356 return nodeName( event.target, "a" ); 5357 } 5358 }, 5359 5360 beforeunload: { 5361 postDispatch: function( event ) { 5362 5363 // Support: Firefox 20+ 5364 // Firefox doesn't alert if the returnValue field is not set. 5365 if ( event.result !== undefined && event.originalEvent ) { 5366 event.originalEvent.returnValue = event.result; 5367 } 5368 } 5369 } 5370 } 5371}; 5372 5373jQuery.removeEvent = function( elem, type, handle ) { 5374 5375 // This "if" is needed for plain objects 5376 if ( elem.removeEventListener ) { 5377 elem.removeEventListener( type, handle ); 5378 } 5379}; 5380 5381jQuery.Event = function( src, props ) { 5382 5383 // Allow instantiation without the 'new' keyword 5384 if ( !( this instanceof jQuery.Event ) ) { 5385 return new jQuery.Event( src, props ); 5386 } 5387 5388 // Event object 5389 if ( src && src.type ) { 5390 this.originalEvent = src; 5391 this.type = src.type; 5392 5393 // Events bubbling up the document may have been marked as prevented 5394 // by a handler lower down the tree; reflect the correct value. 5395 this.isDefaultPrevented = src.defaultPrevented || 5396 src.defaultPrevented === undefined && 5397 5398 // Support: Android <=2.3 only 5399 src.returnValue === false ? 5400 returnTrue : 5401 returnFalse; 5402 5403 // Create target properties 5404 // Support: Safari <=6 - 7 only 5405 // Target should not be a text node (#504, #13143) 5406 this.target = ( src.target && src.target.nodeType === 3 ) ? 5407 src.target.parentNode : 5408 src.target; 5409 5410 this.currentTarget = src.currentTarget; 5411 this.relatedTarget = src.relatedTarget; 5412 5413 // Event type 5414 } else { 5415 this.type = src; 5416 } 5417 5418 // Put explicitly provided properties onto the event object 5419 if ( props ) { 5420 jQuery.extend( this, props ); 5421 } 5422 5423 // Create a timestamp if incoming event doesn't have one 5424 this.timeStamp = src && src.timeStamp || jQuery.now(); 5425 5426 // Mark it as fixed 5427 this[ jQuery.expando ] = true; 5428}; 5429 5430// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding 5431// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html 5432jQuery.Event.prototype = { 5433 constructor: jQuery.Event, 5434 isDefaultPrevented: returnFalse, 5435 isPropagationStopped: returnFalse, 5436 isImmediatePropagationStopped: returnFalse, 5437 isSimulated: false, 5438 5439 preventDefault: function() { 5440 var e = this.originalEvent; 5441 5442 this.isDefaultPrevented = returnTrue; 5443 5444 if ( e && !this.isSimulated ) { 5445 e.preventDefault(); 5446 } 5447 }, 5448 stopPropagation: function() { 5449 var e = this.originalEvent; 5450 5451 this.isPropagationStopped = returnTrue; 5452 5453 if ( e && !this.isSimulated ) { 5454 e.stopPropagation(); 5455 } 5456 }, 5457 stopImmediatePropagation: function() { 5458 var e = this.originalEvent; 5459 5460 this.isImmediatePropagationStopped = returnTrue; 5461 5462 if ( e && !this.isSimulated ) { 5463 e.stopImmediatePropagation(); 5464 } 5465 5466 this.stopPropagation(); 5467 } 5468}; 5469 5470// Includes all common event props including KeyEvent and MouseEvent specific props 5471jQuery.each( { 5472 altKey: true, 5473 bubbles: true, 5474 cancelable: true, 5475 changedTouches: true, 5476 ctrlKey: true, 5477 detail: true, 5478 eventPhase: true, 5479 metaKey: true, 5480 pageX: true, 5481 pageY: true, 5482 shiftKey: true, 5483 view: true, 5484 "char": true, 5485 charCode: true, 5486 key: true, 5487 keyCode: true, 5488 button: true, 5489 buttons: true, 5490 clientX: true, 5491 clientY: true, 5492 offsetX: true, 5493 offsetY: true, 5494 pointerId: true, 5495 pointerType: true, 5496 screenX: true, 5497 screenY: true, 5498 targetTouches: true, 5499 toElement: true, 5500 touches: true, 5501 5502 which: function( event ) { 5503 var button = event.button; 5504 5505 // Add which for key events 5506 if ( event.which == null && rkeyEvent.test( event.type ) ) { 5507 return event.charCode != null ? event.charCode : event.keyCode; 5508 } 5509 5510 // Add which for click: 1 === left; 2 === middle; 3 === right 5511 if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { 5512 if ( button & 1 ) { 5513 return 1; 5514 } 5515 5516 if ( button & 2 ) { 5517 return 3; 5518 } 5519 5520 if ( button & 4 ) { 5521 return 2; 5522 } 5523 5524 return 0; 5525 } 5526 5527 return event.which; 5528 } 5529}, jQuery.event.addProp ); 5530 5531// Create mouseenter/leave events using mouseover/out and event-time checks 5532// so that event delegation works in jQuery. 5533// Do the same for pointerenter/pointerleave and pointerover/pointerout 5534// 5535// Support: Safari 7 only 5536// Safari sends mouseenter too often; see: 5537// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 5538// for the description of the bug (it existed in older Chrome versions as well). 5539jQuery.each( { 5540 mouseenter: "mouseover", 5541 mouseleave: "mouseout", 5542 pointerenter: "pointerover", 5543 pointerleave: "pointerout" 5544}, function( orig, fix ) { 5545 jQuery.event.special[ orig ] = { 5546 delegateType: fix, 5547 bindType: fix, 5548 5549 handle: function( event ) { 5550 var ret, 5551 target = this, 5552 related = event.relatedTarget, 5553 handleObj = event.handleObj; 5554 5555 // For mouseenter/leave call the handler if related is outside the target. 5556 // NB: No relatedTarget if the mouse left/entered the browser window 5557 if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { 5558 event.type = handleObj.origType; 5559 ret = handleObj.handler.apply( this, arguments ); 5560 event.type = fix; 5561 } 5562 return ret; 5563 } 5564 }; 5565} ); 5566 5567jQuery.fn.extend( { 5568 5569 on: function( types, selector, data, fn ) { 5570 return on( this, types, selector, data, fn ); 5571 }, 5572 one: function( types, selector, data, fn ) { 5573 return on( this, types, selector, data, fn, 1 ); 5574 }, 5575 off: function( types, selector, fn ) { 5576 var handleObj, type; 5577 if ( types && types.preventDefault && types.handleObj ) { 5578 5579 // ( event ) dispatched jQuery.Event 5580 handleObj = types.handleObj; 5581 jQuery( types.delegateTarget ).off( 5582 handleObj.namespace ? 5583 handleObj.origType + "." + handleObj.namespace : 5584 handleObj.origType, 5585 handleObj.selector, 5586 handleObj.handler 5587 ); 5588 return this; 5589 } 5590 if ( typeof types === "object" ) { 5591 5592 // ( types-object [, selector] ) 5593 for ( type in types ) { 5594 this.off( type, selector, types[ type ] ); 5595 } 5596 return this; 5597 } 5598 if ( selector === false || typeof selector === "function" ) { 5599 5600 // ( types [, fn] ) 5601 fn = selector; 5602 selector = undefined; 5603 } 5604 if ( fn === false ) { 5605 fn = returnFalse; 5606 } 5607 return this.each( function() { 5608 jQuery.event.remove( this, types, fn, selector ); 5609 } ); 5610 } 5611} ); 5612 5613 5614var 5615 5616 /* eslint-disable max-len */ 5617 5618 // See https://github.com/eslint/eslint/issues/3229 5619 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, 5620 5621 /* eslint-enable */ 5622 5623 // Support: IE <=10 - 11, Edge 12 - 13 5624 // In IE/Edge using regex groups here causes severe slowdowns. 5625 // See https://connect.microsoft.com/IE/feedback/details/1736512/ 5626 rnoInnerhtml = /<script|<style|<link/i, 5627 5628 // checked="checked" or checked 5629 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, 5630 rscriptTypeMasked = /^true\/(.*)/, 5631 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g; 5632 5633// Prefer a tbody over its parent table for containing new rows 5634function manipulationTarget( elem, content ) { 5635 if ( nodeName( elem, "table" ) && 5636 nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { 5637 5638 return jQuery( ">tbody", elem
vendor: 6,531 bytes, lines 5638-5886
5638 )[ 0 ] || elem; 5639 } 5640 5641 return elem; 5642} 5643 5644// Replace/restore the type attribute of script elements for safe DOM manipulation 5645function disableScript( elem ) { 5646 elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; 5647 return elem; 5648} 5649function restoreScript( elem ) { 5650 var match = rscriptTypeMasked.exec( elem.type ); 5651 5652 if ( match ) { 5653 elem.type = match[ 1 ]; 5654 } else { 5655 elem.removeAttribute( "type" ); 5656 } 5657 5658 return elem; 5659} 5660 5661function cloneCopyEvent( src, dest ) { 5662 var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; 5663 5664 if ( dest.nodeType !== 1 ) { 5665 return; 5666 } 5667 5668 // 1. Copy private data: events, handlers, etc. 5669 if ( dataPriv.hasData( src ) ) { 5670 pdataOld = dataPriv.access( src ); 5671 pdataCur = dataPriv.set( dest, pdataOld ); 5672 events = pdataOld.events; 5673 5674 if ( events ) { 5675 delete pdataCur.handle; 5676 pdataCur.events = {}; 5677 5678 for ( type in events ) { 5679 for ( i = 0, l = events[ type ].length; i < l; i++ ) { 5680 jQuery.event.add( dest, type, events[ type ][ i ] ); 5681 } 5682 } 5683 } 5684 } 5685 5686 // 2. Copy user data 5687 if ( dataUser.hasData( src ) ) { 5688 udataOld = dataUser.access( src ); 5689 udataCur = jQuery.extend( {}, udataOld ); 5690 5691 dataUser.set( dest, udataCur ); 5692 } 5693} 5694 5695// Fix IE bugs, see support tests 5696function fixInput( src, dest ) { 5697 var nodeName = dest.nodeName.toLowerCase(); 5698 5699 // Fails to persist the checked state of a cloned checkbox or radio button. 5700 if ( nodeName === "input" && rcheckableType.test( src.type ) ) { 5701 dest.checked = src.checked; 5702 5703 // Fails to return the selected option to the default selected state when cloning options 5704 } else if ( nodeName === "input" || nodeName === "textarea" ) { 5705 dest.defaultValue = src.defaultValue; 5706 } 5707} 5708 5709function domManip( collection, args, callback, ignored ) { 5710 5711 // Flatten any nested arrays 5712 args = concat.apply( [], args ); 5713 5714 var fragment, first, scripts, hasScripts, node, doc, 5715 i = 0, 5716 l = collection.length, 5717 iNoClone = l - 1, 5718 value = args[ 0 ], 5719 isFunction = jQuery.isFunction( value ); 5720 5721 // We can't cloneNode fragments that contain checked, in WebKit 5722 if ( isFunction || 5723 ( l > 1 && typeof value === "string" && 5724 !support.checkClone && rchecked.test( value ) ) ) { 5725 return collection.each( function( index ) { 5726 var self = collection.eq( index ); 5727 if ( isFunction ) { 5728 args[ 0 ] = value.call( this, index, self.html() ); 5729 } 5730 domManip( self, args, callback, ignored ); 5731 } ); 5732 } 5733 5734 if ( l ) { 5735 fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); 5736 first = fragment.firstChild; 5737 5738 if ( fragment.childNodes.length === 1 ) { 5739 fragment = first; 5740 } 5741 5742 // Require either new content or an interest in ignored elements to invoke the callback 5743 if ( first || ignored ) { 5744 scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); 5745 hasScripts = scripts.length; 5746 5747 // Use the original fragment for the last item 5748 // instead of the first because it can end up 5749 // being emptied incorrectly in certain situations (#8070). 5750 for ( ; i < l; i++ ) { 5751 node = fragment; 5752 5753 if ( i !== iNoClone ) { 5754 node = jQuery.clone( node, true, true ); 5755 5756 // Keep references to cloned scripts for later restoration 5757 if ( hasScripts ) { 5758 5759 // Support: Android <=4.0 only, PhantomJS 1 only 5760 // push.apply(_, arraylike) throws on ancient WebKit 5761 jQuery.merge( scripts, getAll( node, "script" ) ); 5762 } 5763 } 5764 5765 callback.call( collection[ i ], node, i ); 5766 } 5767 5768 if ( hasScripts ) { 5769 doc = scripts[ scripts.length - 1 ].ownerDocument; 5770 5771 // Reenable scripts 5772 jQuery.map( scripts, restoreScript ); 5773 5774 // Evaluate executable scripts on first document insertion 5775 for ( i = 0; i < hasScripts; i++ ) { 5776 node = scripts[ i ]; 5777 if ( rscriptType.test( node.type || "" ) && 5778 !dataPriv.access( node, "globalEval" ) && 5779 jQuery.contains( doc, node ) ) { 5780 5781 if ( node.src ) { 5782 5783 // Optional AJAX dependency, but won't run scripts if not present 5784 if ( jQuery._evalUrl ) { 5785 jQuery._evalUrl( node.src ); 5786 } 5787 } else { 5788 DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); 5789 } 5790 } 5791 } 5792 } 5793 } 5794 } 5795 5796 return collection; 5797} 5798 5799function remove( elem, selector, keepData ) { 5800 var node, 5801 nodes = selector ? jQuery.filter( selector, elem ) : elem, 5802 i = 0; 5803 5804 for ( ; ( node = nodes[ i ] ) != null; i++ ) { 5805 if ( !keepData && node.nodeType === 1 ) { 5806 jQuery.cleanData( getAll( node ) ); 5807 } 5808 5809 if ( node.parentNode ) { 5810 if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { 5811 setGlobalEval( getAll( node, "script" ) ); 5812 } 5813 node.parentNode.removeChild( node ); 5814 } 5815 } 5816 5817 return elem; 5818} 5819 5820jQuery.extend( { 5821 htmlPrefilter: function( html ) { 5822 return html.replace( rxhtmlTag, "<$1></$2>" ); 5823 }, 5824 5825 clone: function( elem, dataAndEvents, deepDataAndEvents ) { 5826 var i, l, srcElements, destElements, 5827 clone = elem.cloneNode( true ), 5828 inPage = jQuery.contains( elem.ownerDocument, elem ); 5829 5830 // Fix IE cloning issues 5831 if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && 5832 !jQuery.isXMLDoc( elem ) ) { 5833 5834 // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 5835 destElements = getAll( clone ); 5836 srcElements = getAll( elem ); 5837 5838 for ( i = 0, l = srcElements.length; i < l; i++ ) { 5839 fixInput( srcElements[ i ], destElements[ i ] ); 5840 } 5841 } 5842 5843 // Copy the events from the original to the clone 5844 if ( dataAndEvents ) { 5845 if ( deepDataAndEvents ) { 5846 srcElements = srcElements || getAll( elem ); 5847 destElements = destElements || getAll( clone ); 5848 5849 for ( i = 0, l = srcElements.length; i < l; i++ ) { 5850 cloneCopyEvent( srcElements[ i ], destElements[ i ] ); 5851 } 5852 } else { 5853 cloneCopyEvent( elem, clone ); 5854 } 5855 } 5856 5857 // Preserve script evaluation history 5858 destElements = getAll( clone, "script" ); 5859 if ( destElements.length > 0 ) { 5860 setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); 5861 } 5862 5863 // Return the cloned set 5864 return clone; 5865 }, 5866 5867 cleanData: function( elems ) { 5868 var data, elem, type, 5869 special = jQuery.event.special, 5870 i = 0; 5871 5872 for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { 5873 if ( acceptData( elem ) ) { 5874 if ( ( data = elem[ dataPriv.expando ] ) ) { 5875 if ( data.events ) { 5876 for ( type in data.events ) { 5877 if ( special[ type ] ) { 5878 jQuery.event.remove( elem, type ); 5879 5880 // This is a shortcut to avoid jQuery.event.remove's overhead 5881 } else { 5882 jQuery.removeEvent( elem, type, data.handle ); 5883 } 5884 } 5885 } 5886
vendor: 1,119 bytes, lines 5887-5931
5887 // Support: Chrome <=35 - 45+ 5888 // Assign undefined instead of using delete, see Data#remove 5889 elem[ dataPriv.expando ] = undefined; 5890 } 5891 if ( elem[ dataUser.expando ] ) { 5892 5893 // Support: Chrome <=35 - 45+ 5894 // Assign undefined instead of using delete, see Data#remove 5895 elem[ dataUser.expando ] = undefined; 5896 } 5897 } 5898 } 5899 } 5900} ); 5901 5902jQuery.fn.extend( { 5903 detach: function( selector ) { 5904 return remove( this, selector, true ); 5905 }, 5906 5907 remove: function( selector ) { 5908 return remove( this, selector ); 5909 }, 5910 5911 text: function( value ) { 5912 return access( this, function( value ) { 5913 return value === undefined ? 5914 jQuery.text( this ) : 5915 this.empty().each( function() { 5916 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { 5917 this.textContent = value; 5918 } 5919 } ); 5920 }, null, value, arguments.length ); 5921 }, 5922 5923 append: function() { 5924 return domManip( this, arguments, function( elem ) { 5925 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { 5926 var target = manipulationTarget( this, elem ); 5927 target.appendChild( elem ); 5928 } 5929 } ); 5930 }, 5931
vendor: 6,076 bytes, lines 5932-6167
5932 prepend: function() { 5933 return domManip( this, arguments, function( elem ) { 5934 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { 5935 var target = manipulationTarget( this, elem ); 5936 target.insertBefore( elem, target.firstChild ); 5937 } 5938 } ); 5939 }, 5940 5941 before: function() { 5942 return domManip( this, arguments, function( elem ) { 5943 if ( this.parentNode ) { 5944 this.parentNode.insertBefore( elem, this ); 5945 } 5946 } ); 5947 }, 5948 5949 after: function() { 5950 return domManip( this, arguments, function( elem ) { 5951 if ( this.parentNode ) { 5952 this.parentNode.insertBefore( elem, this.nextSibling ); 5953 } 5954 } ); 5955 }, 5956 5957 empty: function() { 5958 var elem, 5959 i = 0; 5960 5961 for ( ; ( elem = this[ i ] ) != null; i++ ) { 5962 if ( elem.nodeType === 1 ) { 5963 5964 // Prevent memory leaks 5965 jQuery.cleanData( getAll( elem, false ) ); 5966 5967 // Remove any remaining nodes 5968 elem.textContent = ""; 5969 } 5970 } 5971 5972 return this; 5973 }, 5974 5975 clone: function( dataAndEvents, deepDataAndEvents ) { 5976 dataAndEvents = dataAndEvents == null ? false : dataAndEvents; 5977 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; 5978 5979 return this.map( function() { 5980 return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); 5981 } ); 5982 }, 5983 5984 html: function( value ) { 5985 return access( this, function( value ) { 5986 var elem = this[ 0 ] || {}, 5987 i = 0, 5988 l = this.length; 5989 5990 if ( value === undefined && elem.nodeType === 1 ) { 5991 return elem.innerHTML; 5992 } 5993 5994 // See if we can take a shortcut and just use innerHTML 5995 if ( typeof value === "string" && !rnoInnerhtml.test( value ) && 5996 !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { 5997 5998 value = jQuery.htmlPrefilter( value ); 5999 6000 try { 6001 for ( ; i < l; i++ ) { 6002 elem = this[ i ] || {}; 6003 6004 // Remove element nodes and prevent memory leaks 6005 if ( elem.nodeType === 1 ) { 6006 jQuery.cleanData( getAll( elem, false ) ); 6007 elem.innerHTML = value; 6008 } 6009 } 6010 6011 elem = 0; 6012 6013 // If using innerHTML throws an exception, use the fallback method 6014 } catch ( e ) {} 6015 } 6016 6017 if ( elem ) { 6018 this.empty().append( value ); 6019 } 6020 }, null, value, arguments.length ); 6021 }, 6022 6023 replaceWith: function() { 6024 var ignored = []; 6025 6026 // Make the changes, replacing each non-ignored context element with the new content 6027 return domManip( this, arguments, function( elem ) { 6028 var parent = this.parentNode; 6029 6030 if ( jQuery.inArray( this, ignored ) < 0 ) { 6031 jQuery.cleanData( getAll( this ) ); 6032 if ( parent ) { 6033 parent.replaceChild( elem, this ); 6034 } 6035 } 6036 6037 // Force callback invocation 6038 }, ignored ); 6039 } 6040} ); 6041 6042jQuery.each( { 6043 appendTo: "append", 6044 prependTo: "prepend", 6045 insertBefore: "before", 6046 insertAfter: "after", 6047 replaceAll: "replaceWith" 6048}, function( name, original ) { 6049 jQuery.fn[ name ] = function( selector ) { 6050 var elems, 6051 ret = [], 6052 insert = jQuery( selector ), 6053 last = insert.length - 1, 6054 i = 0; 6055 6056 for ( ; i <= last; i++ ) { 6057 elems = i === last ? this : this.clone( true ); 6058 jQuery( insert[ i ] )[ original ]( elems ); 6059 6060 // Support: Android <=4.0 only, PhantomJS 1 only 6061 // .get() because push.apply(_, arraylike) throws on ancient WebKit 6062 push.apply( ret, elems.get() ); 6063 } 6064 6065 return this.pushStack( ret ); 6066 }; 6067} ); 6068var rmargin = ( /^margin/ ); 6069 6070var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); 6071 6072var getStyles = function( elem ) { 6073 6074 // Support: IE <=11 only, Firefox <=30 (#15098, #14150) 6075 // IE throws on elements created in popups 6076 // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" 6077 var view = elem.ownerDocument.defaultView; 6078 6079 if ( !view || !view.opener ) { 6080 view = window; 6081 } 6082 6083 return view.getComputedStyle( elem ); 6084 }; 6085 6086 6087 6088( function() { 6089 6090 // Executing both pixelPosition & boxSizingReliable tests require only one layout 6091 // so they're executed at the same time to save the second computation. 6092 function computeStyleTests() { 6093 6094 // This is a singleton, we need to execute it only once 6095 if ( !div ) { 6096 return; 6097 } 6098 6099 div.style.cssText = 6100 "box-sizing:border-box;" + 6101 "position:relative;display:block;" + 6102 "margin:auto;border:1px;padding:1px;" + 6103 "top:1%;width:50%"; 6104 div.innerHTML = ""; 6105 documentElement.appendChild( container ); 6106 6107 var divStyle = window.getComputedStyle( div ); 6108 pixelPositionVal = divStyle.top !== "1%"; 6109 6110 // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 6111 reliableMarginLeftVal = divStyle.marginLeft === "2px"; 6112 boxSizingReliableVal = divStyle.width === "4px"; 6113 6114 // Support: Android 4.0 - 4.3 only 6115 // Some styles come back with percentage values, even though they shouldn't 6116 div.style.marginRight = "50%"; 6117 pixelMarginRightVal = divStyle.marginRight === "4px"; 6118 6119 documentElement.removeChild( container ); 6120 6121 // Nullify the div so it wouldn't be stored in the memory and 6122 // it will also be a sign that checks already performed 6123 div = null; 6124 } 6125 6126 var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal, 6127 container = document.createElement( "div" ), 6128 div = document.createElement( "div" ); 6129 6130 // Finish early in limited (non-browser) environments 6131 if ( !div.style ) { 6132 return; 6133 } 6134 6135 // Support: IE <=9 - 11 only 6136 // Style of cloned element affects source element cloned (#8908) 6137 div.style.backgroundClip = "content-box"; 6138 div.cloneNode( true ).style.backgroundClip = ""; 6139 support.clearCloneStyle = div.style.backgroundClip === "content-box"; 6140 6141 container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" + 6142 "padding:0;margin-top:1px;position:absolute"; 6143 container.appendChild( div ); 6144 6145 jQuery.extend( support, { 6146 pixelPosition: function() { 6147 computeStyleTests(); 6148 return pixelPositionVal; 6149 }, 6150 boxSizingReliable: function() { 6151 computeStyleTests(); 6152 return boxSizingReliableVal; 6153 }, 6154 pixelMarginRight: function() { 6155 computeStyleTests(); 6156 return pixelMarginRightVal; 6157 }, 6158 reliableMarginLeft: function() { 6159 computeStyleTests(); 6160 return reliableMarginLeftVal; 6161 } 6162 } ); 6163} )(); 6164 6165 6166function curCSS( elem, name, computed ) { 6167 var width, minWidth, maxWidth, ret,
vendor: 8,366 bytes, lines 6168-6473
6168 6169 // Support: Firefox 51+ 6170 // Retrieving style before computed somehow 6171 // fixes an issue with getting wrong values 6172 // on detached elements 6173 style = elem.style; 6174 6175 computed = computed || getStyles( elem ); 6176 6177 // getPropertyValue is needed for: 6178 // .css('filter') (IE 9 only, #12537) 6179 // .css('--customProperty) (#3144) 6180 if ( computed ) { 6181 ret = computed.getPropertyValue( name ) || computed[ name ]; 6182 6183 if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { 6184 ret = jQuery.style( elem, name ); 6185 } 6186 6187 // A tribute to the "awesome hack by Dean Edwards" 6188 // Android Browser returns percentage for some values, 6189 // but width seems to be reliably pixels. 6190 // This is against the CSSOM draft spec: 6191 // https://drafts.csswg.org/cssom/#resolved-values 6192 if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) { 6193 6194 // Remember the original values 6195 width = style.width; 6196 minWidth = style.minWidth; 6197 maxWidth = style.maxWidth; 6198 6199 // Put in the new values to get a computed value out 6200 style.minWidth = style.maxWidth = style.width = ret; 6201 ret = computed.width; 6202 6203 // Revert the changed values 6204 style.width = width; 6205 style.minWidth = minWidth; 6206 style.maxWidth = maxWidth; 6207 } 6208 } 6209 6210 return ret !== undefined ? 6211 6212 // Support: IE <=9 - 11 only 6213 // IE returns zIndex value as an integer. 6214 ret + "" : 6215 ret; 6216} 6217 6218 6219function addGetHookIf( conditionFn, hookFn ) { 6220 6221 // Define the hook, we'll check on the first run if it's really needed. 6222 return { 6223 get: function() { 6224 if ( conditionFn() ) { 6225 6226 // Hook not needed (or it's not possible to use it due 6227 // to missing dependency), remove it. 6228 delete this.get; 6229 return; 6230 } 6231 6232 // Hook needed; redefine it so that the support test is not executed again. 6233 return ( this.get = hookFn ).apply( this, arguments ); 6234 } 6235 }; 6236} 6237 6238 6239var 6240 6241 // Swappable if display is none or starts with table 6242 // except "table", "table-cell", or "table-caption" 6243 // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display 6244 rdisplayswap = /^(none|table(?!-c[ea]).+)/, 6245 rcustomProp = /^--/, 6246 cssShow = { position: "absolute", visibility: "hidden", display: "block" }, 6247 cssNormalTransform = { 6248 letterSpacing: "0", 6249 fontWeight: "400" 6250 }, 6251 6252 cssPrefixes = [ "Webkit", "Moz", "ms" ], 6253 emptyStyle = document.createElement( "div" ).style; 6254 6255// Return a css property mapped to a potentially vendor prefixed property 6256function vendorPropName( name ) { 6257 6258 // Shortcut for names that are not vendor prefixed 6259 if ( name in emptyStyle ) { 6260 return name; 6261 } 6262 6263 // Check for vendor prefixed names 6264 var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), 6265 i = cssPrefixes.length; 6266 6267 while ( i-- ) { 6268 name = cssPrefixes[ i ] + capName; 6269 if ( name in emptyStyle ) { 6270 return name; 6271 } 6272 } 6273} 6274 6275// Return a property mapped along what jQuery.cssProps suggests or to 6276// a vendor prefixed property. 6277function finalPropName( name ) { 6278 var ret = jQuery.cssProps[ name ]; 6279 if ( !ret ) { 6280 ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; 6281 } 6282 return ret; 6283} 6284 6285function setPositiveNumber( elem, value, subtract ) { 6286 6287 // Any relative (+/-) values have already been 6288 // normalized at this point 6289 var matches = rcssNum.exec( value ); 6290 return matches ? 6291 6292 // Guard against undefined "subtract", e.g., when used as in cssHooks 6293 Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : 6294 value; 6295} 6296 6297function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { 6298 var i, 6299 val = 0; 6300 6301 // If we already have the right measurement, avoid augmentation 6302 if ( extra === ( isBorderBox ? "border" : "content" ) ) { 6303 i = 4; 6304 6305 // Otherwise initialize for horizontal or vertical properties 6306 } else { 6307 i = name === "width" ? 1 : 0; 6308 } 6309 6310 for ( ; i < 4; i += 2 ) { 6311 6312 // Both box models exclude margin, so add it if we want it 6313 if ( extra === "margin" ) { 6314 val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); 6315 } 6316 6317 if ( isBorderBox ) { 6318 6319 // border-box includes padding, so remove it if we want content 6320 if ( extra === "content" ) { 6321 val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); 6322 } 6323 6324 // At this point, extra isn't border nor margin, so remove border 6325 if ( extra !== "margin" ) { 6326 val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); 6327 } 6328 } else { 6329 6330 // At this point, extra isn't content, so add padding 6331 val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); 6332 6333 // At this point, extra isn't content nor padding, so add border 6334 if ( extra !== "padding" ) { 6335 val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); 6336 } 6337 } 6338 } 6339 6340 return val; 6341} 6342 6343function getWidthOrHeight( elem, name, extra ) { 6344 6345 // Start with computed style 6346 var valueIsBorderBox, 6347 styles = getStyles( elem ), 6348 val = curCSS( elem, name, styles ), 6349 isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; 6350 6351 // Computed unit is not pixels. Stop here and return. 6352 if ( rnumnonpx.test( val ) ) { 6353 return val; 6354 } 6355 6356 // Check for style in case a browser which returns unreliable values 6357 // for getComputedStyle silently falls back to the reliable elem.style 6358 valueIsBorderBox = isBorderBox && 6359 ( support.boxSizingReliable() || val === elem.style[ name ] ); 6360 6361 // Fall back to offsetWidth/Height when value is "auto" 6362 // This happens for inline elements with no explicit setting (gh-3571) 6363 if ( val === "auto" ) { 6364 val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ]; 6365 } 6366 6367 // Normalize "", auto, and prepare for extra 6368 val = parseFloat( val ) || 0; 6369 6370 // Use the active box-sizing model to add/subtract irrelevant styles 6371 return ( val + 6372 augmentWidthOrHeight( 6373 elem, 6374 name, 6375 extra || ( isBorderBox ? "border" : "content" ), 6376 valueIsBorderBox, 6377 styles 6378 ) 6379 ) + "px"; 6380} 6381 6382jQuery.extend( { 6383 6384 // Add in style property hooks for overriding the default 6385 // behavior of getting and setting a style property 6386 cssHooks: { 6387 opacity: { 6388 get: function( elem, computed ) { 6389 if ( computed ) { 6390 6391 // We should always get a number back from opacity 6392 var ret = curCSS( elem, "opacity" ); 6393 return ret === "" ? "1" : ret; 6394 } 6395 } 6396 } 6397 }, 6398 6399 // Don't automatically add "px" to these possibly-unitless properties 6400 cssNumber: { 6401 "animationIterationCount": true, 6402 "columnCount": true, 6403 "fillOpacity": true, 6404 "flexGrow": true, 6405 "flexShrink": true, 6406 "fontWeight": true, 6407 "lineHeight": true, 6408 "opacity": true, 6409 "order": true, 6410 "orphans": true, 6411 "widows": true, 6412 "zIndex": true, 6413 "zoom": true 6414 }, 6415 6416 // Add in properties whose names you wish to fix before 6417 // setting or getting the value 6418 cssProps: { 6419 "float": "cssFloat" 6420 }, 6421 6422 // Get and set the style property on a DOM Node 6423 style: function( elem, name, value, extra ) { 6424 6425 // Don't set styles on text and comment nodes 6426 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { 6427 return; 6428 } 6429 6430 // Make sure that we're working with the right name 6431 var ret, type, hooks, 6432 origName = jQuery.camelCase( name ), 6433 isCustomProp = rcustomProp.test( name ), 6434 style = elem.style; 6435 6436 // Make sure that we're working with the right name. We don't 6437 // want to query the value if it is a CSS custom property 6438 // since they are user-defined. 6439 if ( !isCustomProp ) { 6440 name = finalPropName( origName ); 6441 } 6442 6443 // Gets hook for the prefixed version, then unprefixed version 6444 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; 6445 6446 // Check if we're setting a value 6447 if ( value !== undefined ) { 6448 type = typeof value; 6449 6450 // Convert "+=" or "-=" to relative numbers (#7345) 6451 if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { 6452 value = adjustCSS( elem, name, ret ); 6453 6454 // Fixes bug #9237 6455 type = "number"; 6456 } 6457 6458 // Make sure that null and NaN values aren't set (#7116) 6459 if ( value == null || value !== value ) { 6460 return; 6461 } 6462 6463 // If a number was passed in, add the unit (except for certain CSS properties) 6464 if ( type === "number" ) { 6465 value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); 6466 } 6467 6468 // background-* props affect original clone's values 6469 if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { 6470 style[ name ] = "inherit"; 6471 } 6472 6473 // If a hook was provided, use that value, otherwise just set the specified value
vendor: 4,779 bytes, lines 6474-6656
6474 if ( !hooks || !( "set" in hooks ) || 6475 ( value = hooks.set( elem, value, extra ) ) !== undefined ) { 6476 6477 if ( isCustomProp ) { 6478 style.setProperty( name, value ); 6479 } else { 6480 style[ name ] = value; 6481 } 6482 } 6483 6484 } else { 6485 6486 // If a hook was provided get the non-computed value from there 6487 if ( hooks && "get" in hooks && 6488 ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { 6489 6490 return ret; 6491 } 6492 6493 // Otherwise just get the value from the style object 6494 return style[ name ]; 6495 } 6496 }, 6497 6498 css: function( elem, name, extra, styles ) { 6499 var val, num, hooks, 6500 origName = jQuery.camelCase( name ), 6501 isCustomProp = rcustomProp.test( name ); 6502 6503 // Make sure that we're working with the right name. We don't 6504 // want to modify the value if it is a CSS custom property 6505 // since they are user-defined. 6506 if ( !isCustomProp ) { 6507 name = finalPropName( origName ); 6508 } 6509 6510 // Try prefixed name followed by the unprefixed name 6511 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; 6512 6513 // If a hook was provided get the computed value from there 6514 if ( hooks && "get" in hooks ) { 6515 val = hooks.get( elem, true, extra ); 6516 } 6517 6518 // Otherwise, if a way to get the computed value exists, use that 6519 if ( val === undefined ) { 6520 val = curCSS( elem, name, styles ); 6521 } 6522 6523 // Convert "normal" to computed value 6524 if ( val === "normal" && name in cssNormalTransform ) { 6525 val = cssNormalTransform[ name ]; 6526 } 6527 6528 // Make numeric if forced or a qualifier was provided and val looks numeric 6529 if ( extra === "" || extra ) { 6530 num = parseFloat( val ); 6531 return extra === true || isFinite( num ) ? num || 0 : val; 6532 } 6533 6534 return val; 6535 } 6536} ); 6537 6538jQuery.each( [ "height", "width" ], function( i, name ) { 6539 jQuery.cssHooks[ name ] = { 6540 get: function( elem, computed, extra ) { 6541 if ( computed ) { 6542 6543 // Certain elements can have dimension info if we invisibly show them 6544 // but it must have a current display style that would benefit 6545 return rdisplayswap.test( jQuery.css( elem, "display" ) ) && 6546 6547 // Support: Safari 8+ 6548 // Table columns in Safari have non-zero offsetWidth & zero 6549 // getBoundingClientRect().width unless display is changed. 6550 // Support: IE <=11 only 6551 // Running getBoundingClientRect on a disconnected node 6552 // in IE throws an error. 6553 ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? 6554 swap( elem, cssShow, function() { 6555 return getWidthOrHeight( elem, name, extra ); 6556 } ) : 6557 getWidthOrHeight( elem, name, extra ); 6558 } 6559 }, 6560 6561 set: function( elem, value, extra ) { 6562 var matches, 6563 styles = extra && getStyles( elem ), 6564 subtract = extra && augmentWidthOrHeight( 6565 elem, 6566 name, 6567 extra, 6568 jQuery.css( elem, "boxSizing", false, styles ) === "border-box", 6569 styles 6570 ); 6571 6572 // Convert to pixels if value adjustment is needed 6573 if ( subtract && ( matches = rcssNum.exec( value ) ) && 6574 ( matches[ 3 ] || "px" ) !== "px" ) { 6575 6576 elem.style[ name ] = value; 6577 value = jQuery.css( elem, name ); 6578 } 6579 6580 return setPositiveNumber( elem, value, subtract ); 6581 } 6582 }; 6583} ); 6584 6585jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, 6586 function( elem, computed ) { 6587 if ( computed ) { 6588 return ( parseFloat( curCSS( elem, "marginLeft" ) ) || 6589 elem.getBoundingClientRect().left - 6590 swap( elem, { marginLeft: 0 }, function() { 6591 return elem.getBoundingClientRect().left; 6592 } ) 6593 ) + "px"; 6594 } 6595 } 6596); 6597 6598// These hooks are used by animate to expand properties 6599jQuery.each( { 6600 margin: "", 6601 padding: "", 6602 border: "Width" 6603}, function( prefix, suffix ) { 6604 jQuery.cssHooks[ prefix + suffix ] = { 6605 expand: function( value ) { 6606 var i = 0, 6607 expanded = {}, 6608 6609 // Assumes a single number if not a string 6610 parts = typeof value === "string" ? value.split( " " ) : [ value ]; 6611 6612 for ( ; i < 4; i++ ) { 6613 expanded[ prefix + cssExpand[ i ] + suffix ] = 6614 parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; 6615 } 6616 6617 return expanded; 6618 } 6619 }; 6620 6621 if ( !rmargin.test( prefix ) ) { 6622 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; 6623 } 6624} ); 6625 6626jQuery.fn.extend( { 6627 css: function( name, value ) { 6628 return access( this, function( elem, name, value ) { 6629 var styles, len, 6630 map = {}, 6631 i = 0; 6632 6633 if ( Array.isArray( name ) ) { 6634 styles = getStyles( elem ); 6635 len = name.length; 6636 6637 for ( ; i < len; i++ ) { 6638 map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); 6639 } 6640 6641 return map; 6642 } 6643 6644 return value !== undefined ? 6645 jQuery.style( elem, name, value ) : 6646 jQuery.css( elem, name ); 6647 }, name, value, arguments.length > 1 ); 6648 } 6649} ); 6650 6651 6652function Tween( elem, options, prop, end, easing ) { 6653 return new Tween.prototype.init( elem, options, prop, end, easing ); 6654} 6655jQuery.Tween = Tween; 6656
vendor: 6,216 bytes, lines 6657-6895
6657Tween.prototype = { 6658 constructor: Tween, 6659 init: function( elem, options, prop, end, easing, unit ) { 6660 this.elem = elem; 6661 this.prop = prop; 6662 this.easing = easing || jQuery.easing._default; 6663 this.options = options; 6664 this.start = this.now = this.cur(); 6665 this.end = end; 6666 this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); 6667 }, 6668 cur: function() { 6669 var hooks = Tween.propHooks[ this.prop ]; 6670 6671 return hooks && hooks.get ? 6672 hooks.get( this ) : 6673 Tween.propHooks._default.get( this ); 6674 }, 6675 run: function( percent ) { 6676 var eased, 6677 hooks = Tween.propHooks[ this.prop ]; 6678 6679 if ( this.options.duration ) { 6680 this.pos = eased = jQuery.easing[ this.easing ]( 6681 percent, this.options.duration * percent, 0, 1, this.options.duration 6682 ); 6683 } else { 6684 this.pos = eased = percent; 6685 } 6686 this.now = ( this.end - this.start ) * eased + this.start; 6687 6688 if ( this.options.step ) { 6689 this.options.step.call( this.elem, this.now, this ); 6690 } 6691 6692 if ( hooks && hooks.set ) { 6693 hooks.set( this ); 6694 } else { 6695 Tween.propHooks._default.set( this ); 6696 } 6697 return this; 6698 } 6699}; 6700 6701Tween.prototype.init.prototype = Tween.prototype; 6702 6703Tween.propHooks = { 6704 _default: { 6705 get: function( tween ) { 6706 var result; 6707 6708 // Use a property on the element directly when it is not a DOM element, 6709 // or when there is no matching style property that exists. 6710 if ( tween.elem.nodeType !== 1 || 6711 tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { 6712 return tween.elem[ tween.prop ]; 6713 } 6714 6715 // Passing an empty string as a 3rd parameter to .css will automatically 6716 // attempt a parseFloat and fallback to a string if the parse fails. 6717 // Simple values such as "10px" are parsed to Float; 6718 // complex values such as "rotate(1rad)" are returned as-is. 6719 result = jQuery.css( tween.elem, tween.prop, "" ); 6720 6721 // Empty strings, null, undefined and "auto" are converted to 0. 6722 return !result || result === "auto" ? 0 : result; 6723 }, 6724 set: function( tween ) { 6725 6726 // Use step hook for back compat. 6727 // Use cssHook if its there. 6728 // Use .style if available and use plain properties where available. 6729 if ( jQuery.fx.step[ tween.prop ] ) { 6730 jQuery.fx.step[ tween.prop ]( tween ); 6731 } else if ( tween.elem.nodeType === 1 && 6732 ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || 6733 jQuery.cssHooks[ tween.prop ] ) ) { 6734 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); 6735 } else { 6736 tween.elem[ tween.prop ] = tween.now; 6737 } 6738 } 6739 } 6740}; 6741 6742// Support: IE <=9 only 6743// Panic based approach to setting things on disconnected nodes 6744Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { 6745 set: function( tween ) { 6746 if ( tween.elem.nodeType && tween.elem.parentNode ) { 6747 tween.elem[ tween.prop ] = tween.now; 6748 } 6749 } 6750}; 6751 6752jQuery.easing = { 6753 linear: function( p ) { 6754 return p; 6755 }, 6756 swing: function( p ) { 6757 return 0.5 - Math.cos( p * Math.PI ) / 2; 6758 }, 6759 _default: "swing" 6760}; 6761 6762jQuery.fx = Tween.prototype.init; 6763 6764// Back compat <1.8 extension point 6765jQuery.fx.step = {}; 6766 6767 6768 6769 6770var 6771 fxNow, inProgress, 6772 rfxtypes = /^(?:toggle|show|hide)$/, 6773 rrun = /queueHooks$/; 6774 6775function schedule() { 6776 if ( inProgress ) { 6777 if ( document.hidden === false && window.requestAnimationFrame ) { 6778 window.requestAnimationFrame( schedule ); 6779 } else { 6780 window.setTimeout( schedule, jQuery.fx.interval ); 6781 } 6782 6783 jQuery.fx.tick(); 6784 } 6785} 6786 6787// Animations created synchronously will run synchronously 6788function createFxNow() { 6789 window.setTimeout( function() { 6790 fxNow = undefined; 6791 } ); 6792 return ( fxNow = jQuery.now() ); 6793} 6794 6795// Generate parameters to create a standard animation 6796function genFx( type, includeWidth ) { 6797 var which, 6798 i = 0, 6799 attrs = { height: type }; 6800 6801 // If we include width, step value is 1 to do all cssExpand values, 6802 // otherwise step value is 2 to skip over Left and Right 6803 includeWidth = includeWidth ? 1 : 0; 6804 for ( ; i < 4; i += 2 - includeWidth ) { 6805 which = cssExpand[ i ]; 6806 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; 6807 } 6808 6809 if ( includeWidth ) { 6810 attrs.opacity = attrs.width = type; 6811 } 6812 6813 return attrs; 6814} 6815 6816function createTween( value, prop, animation ) { 6817 var tween, 6818 collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), 6819 index = 0, 6820 length = collection.length; 6821 for ( ; index < length; index++ ) { 6822 if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { 6823 6824 // We're done with this property 6825 return tween; 6826 } 6827 } 6828} 6829 6830function defaultPrefilter( elem, props, opts ) { 6831 var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, 6832 isBox = "width" in props || "height" in props, 6833 anim = this, 6834 orig = {}, 6835 style = elem.style, 6836 hidden = elem.nodeType && isHiddenWithinTree( elem ), 6837 dataShow = dataPriv.get( elem, "fxshow" ); 6838 6839 // Queue-skipping animations hijack the fx hooks 6840 if ( !opts.queue ) { 6841 hooks = jQuery._queueHooks( elem, "fx" ); 6842 if ( hooks.unqueued == null ) { 6843 hooks.unqueued = 0; 6844 oldfire = hooks.empty.fire; 6845 hooks.empty.fire = function() { 6846 if ( !hooks.unqueued ) { 6847 oldfire(); 6848 } 6849 }; 6850 } 6851 hooks.unqueued++; 6852 6853 anim.always( function() { 6854 6855 // Ensure the complete handler is called before this completes 6856 anim.always( function() { 6857 hooks.unqueued--; 6858 if ( !jQuery.queue( elem, "fx" ).length ) { 6859 hooks.empty.fire(); 6860 } 6861 } ); 6862 } ); 6863 } 6864 6865 // Detect show/hide animations 6866 for ( prop in props ) { 6867 value = props[ prop ]; 6868 if ( rfxtypes.test( value ) ) { 6869 delete props[ prop ]; 6870 toggle = toggle || value === "toggle"; 6871 if ( value === ( hidden ? "hide" : "show" ) ) { 6872 6873 // Pretend to be hidden if this is a "show" and 6874 // there is still data from a stopped show/hide 6875 if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { 6876 hidden = true; 6877 6878 // Ignore all other no-op show/hide data 6879 } else { 6880 continue; 6881 } 6882 } 6883 orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); 6884 } 6885 } 6886 6887 // Bail out if this is a no-op like .hide().hide() 6888 propTween = !jQuery.isEmptyObject( props ); 6889 if ( !propTween && jQuery.isEmptyObject( orig ) ) { 6890 return; 6891 } 6892 6893 // Restrict "overflow" and "display" styles during box animations 6894 if ( isBox && elem.nodeType === 1 ) { 6895
vendor: 4,173 bytes, lines 6896-7054
6896 // Support: IE <=9 - 11, Edge 12 - 13 6897 // Record all 3 overflow attributes because IE does not infer the shorthand 6898 // from identically-valued overflowX and overflowY 6899 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; 6900 6901 // Identify a display type, preferring old show/hide data over the CSS cascade 6902 restoreDisplay = dataShow && dataShow.display; 6903 if ( restoreDisplay == null ) { 6904 restoreDisplay = dataPriv.get( elem, "display" ); 6905 } 6906 display = jQuery.css( elem, "display" ); 6907 if ( display === "none" ) { 6908 if ( restoreDisplay ) { 6909 display = restoreDisplay; 6910 } else { 6911 6912 // Get nonempty value(s) by temporarily forcing visibility 6913 showHide( [ elem ], true ); 6914 restoreDisplay = elem.style.display || restoreDisplay; 6915 display = jQuery.css( elem, "display" ); 6916 showHide( [ elem ] ); 6917 } 6918 } 6919 6920 // Animate inline elements as inline-block 6921 if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { 6922 if ( jQuery.css( elem, "float" ) === "none" ) { 6923 6924 // Restore the original display value at the end of pure show/hide animations 6925 if ( !propTween ) { 6926 anim.done( function() { 6927 style.display = restoreDisplay; 6928 } ); 6929 if ( restoreDisplay == null ) { 6930 display = style.display; 6931 restoreDisplay = display === "none" ? "" : display; 6932 } 6933 } 6934 style.display = "inline-block"; 6935 } 6936 } 6937 } 6938 6939 if ( opts.overflow ) { 6940 style.overflow = "hidden"; 6941 anim.always( function() { 6942 style.overflow = opts.overflow[ 0 ]; 6943 style.overflowX = opts.overflow[ 1 ]; 6944 style.overflowY = opts.overflow[ 2 ]; 6945 } ); 6946 } 6947 6948 // Implement show/hide animations 6949 propTween = false; 6950 for ( prop in orig ) { 6951 6952 // General show/hide setup for this element animation 6953 if ( !propTween ) { 6954 if ( dataShow ) { 6955 if ( "hidden" in dataShow ) { 6956 hidden = dataShow.hidden; 6957 } 6958 } else { 6959 dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); 6960 } 6961 6962 // Store hidden/visible for toggle so `.stop().toggle()` "reverses" 6963 if ( toggle ) { 6964 dataShow.hidden = !hidden; 6965 } 6966 6967 // Show elements before animating them 6968 if ( hidden ) { 6969 showHide( [ elem ], true ); 6970 } 6971 6972 /* eslint-disable no-loop-func */ 6973 6974 anim.done( function() { 6975 6976 /* eslint-enable no-loop-func */ 6977 6978 // The final step of a "hide" animation is actually hiding the element 6979 if ( !hidden ) { 6980 showHide( [ elem ] ); 6981 } 6982 dataPriv.remove( elem, "fxshow" ); 6983 for ( prop in orig ) { 6984 jQuery.style( elem, prop, orig[ prop ] ); 6985 } 6986 } ); 6987 } 6988 6989 // Per-property setup 6990 propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); 6991 if ( !( prop in dataShow ) ) { 6992 dataShow[ prop ] = propTween.start; 6993 if ( hidden ) { 6994 propTween.end = propTween.start; 6995 propTween.start = 0; 6996 } 6997 } 6998 } 6999} 7000 7001function propFilter( props, specialEasing ) { 7002 var index, name, easing, value, hooks; 7003 7004 // camelCase, specialEasing and expand cssHook pass 7005 for ( index in props ) { 7006 name = jQuery.camelCase( index ); 7007 easing = specialEasing[ name ]; 7008 value = props[ index ]; 7009 if ( Array.isArray( value ) ) { 7010 easing = value[ 1 ]; 7011 value = props[ index ] = value[ 0 ]; 7012 } 7013 7014 if ( index !== name ) { 7015 props[ name ] = value; 7016 delete props[ index ]; 7017 } 7018 7019 hooks = jQuery.cssHooks[ name ]; 7020 if ( hooks && "expand" in hooks ) { 7021 value = hooks.expand( value ); 7022 delete props[ name ]; 7023 7024 // Not quite $.extend, this won't overwrite existing keys. 7025 // Reusing 'index' because we have the correct "name" 7026 for ( index in value ) { 7027 if ( !( index in props ) ) { 7028 props[ index ] = value[ index ]; 7029 specialEasing[ index ] = easing; 7030 } 7031 } 7032 } else { 7033 specialEasing[ name ] = easing; 7034 } 7035 } 7036} 7037 7038function Animation( elem, properties, options ) { 7039 var result, 7040 stopped, 7041 index = 0, 7042 length = Animation.prefilters.length, 7043 deferred = jQuery.Deferred().always( function() { 7044 7045 // Don't match elem in the :animated selector 7046 delete tick.elem; 7047 } ), 7048 tick = function() { 7049 if ( stopped ) { 7050 return false; 7051 } 7052 var currentTime = fxNow || createFxNow(), 7053 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), 7054
vendor: 6,686 bytes, lines 7055-7314
7055 // Support: Android 2.3 only 7056 // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) 7057 temp = remaining / animation.duration || 0, 7058 percent = 1 - temp, 7059 index = 0, 7060 length = animation.tweens.length; 7061 7062 for ( ; index < length; index++ ) { 7063 animation.tweens[ index ].run( percent ); 7064 } 7065 7066 deferred.notifyWith( elem, [ animation, percent, remaining ] ); 7067 7068 // If there's more to do, yield 7069 if ( percent < 1 && length ) { 7070 return remaining; 7071 } 7072 7073 // If this was an empty animation, synthesize a final progress notification 7074 if ( !length ) { 7075 deferred.notifyWith( elem, [ animation, 1, 0 ] ); 7076 } 7077 7078 // Resolve the animation and report its conclusion 7079 deferred.resolveWith( elem, [ animation ] ); 7080 return false; 7081 }, 7082 animation = deferred.promise( { 7083 elem: elem, 7084 props: jQuery.extend( {}, properties ), 7085 opts: jQuery.extend( true, { 7086 specialEasing: {}, 7087 easing: jQuery.easing._default 7088 }, options ), 7089 originalProperties: properties, 7090 originalOptions: options, 7091 startTime: fxNow || createFxNow(), 7092 duration: options.duration, 7093 tweens: [], 7094 createTween: function( prop, end ) { 7095 var tween = jQuery.Tween( elem, animation.opts, prop, end, 7096 animation.opts.specialEasing[ prop ] || animation.opts.easing ); 7097 animation.tweens.push( tween ); 7098 return tween; 7099 }, 7100 stop: function( gotoEnd ) { 7101 var index = 0, 7102 7103 // If we are going to the end, we want to run all the tweens 7104 // otherwise we skip this part 7105 length = gotoEnd ? animation.tweens.length : 0; 7106 if ( stopped ) { 7107 return this; 7108 } 7109 stopped = true; 7110 for ( ; index < length; index++ ) { 7111 animation.tweens[ index ].run( 1 ); 7112 } 7113 7114 // Resolve when we played the last frame; otherwise, reject 7115 if ( gotoEnd ) { 7116 deferred.notifyWith( elem, [ animation, 1, 0 ] ); 7117 deferred.resolveWith( elem, [ animation, gotoEnd ] ); 7118 } else { 7119 deferred.rejectWith( elem, [ animation, gotoEnd ] ); 7120 } 7121 return this; 7122 } 7123 } ), 7124 props = animation.props; 7125 7126 propFilter( props, animation.opts.specialEasing ); 7127 7128 for ( ; index < length; index++ ) { 7129 result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); 7130 if ( result ) { 7131 if ( jQuery.isFunction( result.stop ) ) { 7132 jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = 7133 jQuery.proxy( result.stop, result ); 7134 } 7135 return result; 7136 } 7137 } 7138 7139 jQuery.map( props, createTween, animation ); 7140 7141 if ( jQuery.isFunction( animation.opts.start ) ) { 7142 animation.opts.start.call( elem, animation ); 7143 } 7144 7145 // Attach callbacks from options 7146 animation 7147 .progress( animation.opts.progress ) 7148 .done( animation.opts.done, animation.opts.complete ) 7149 .fail( animation.opts.fail ) 7150 .always( animation.opts.always ); 7151 7152 jQuery.fx.timer( 7153 jQuery.extend( tick, { 7154 elem: elem, 7155 anim: animation, 7156 queue: animation.opts.queue 7157 } ) 7158 ); 7159 7160 return animation; 7161} 7162 7163jQuery.Animation = jQuery.extend( Animation, { 7164 7165 tweeners: { 7166 "*": [ function( prop, value ) { 7167 var tween = this.createTween( prop, value ); 7168 adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); 7169 return tween; 7170 } ] 7171 }, 7172 7173 tweener: function( props, callback ) { 7174 if ( jQuery.isFunction( props ) ) { 7175 callback = props; 7176 props = [ "*" ]; 7177 } else { 7178 props = props.match( rnothtmlwhite ); 7179 } 7180 7181 var prop, 7182 index = 0, 7183 length = props.length; 7184 7185 for ( ; index < length; index++ ) { 7186 prop = props[ index ]; 7187 Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; 7188 Animation.tweeners[ prop ].unshift( callback ); 7189 } 7190 }, 7191 7192 prefilters: [ defaultPrefilter ], 7193 7194 prefilter: function( callback, prepend ) { 7195 if ( prepend ) { 7196 Animation.prefilters.unshift( callback ); 7197 } else { 7198 Animation.prefilters.push( callback ); 7199 } 7200 } 7201} ); 7202 7203jQuery.speed = function( speed, easing, fn ) { 7204 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { 7205 complete: fn || !fn && easing || 7206 jQuery.isFunction( speed ) && speed, 7207 duration: speed, 7208 easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing 7209 }; 7210 7211 // Go to the end state if fx are off 7212 if ( jQuery.fx.off ) { 7213 opt.duration = 0; 7214 7215 } else { 7216 if ( typeof opt.duration !== "number" ) { 7217 if ( opt.duration in jQuery.fx.speeds ) { 7218 opt.duration = jQuery.fx.speeds[ opt.duration ]; 7219 7220 } else { 7221 opt.duration = jQuery.fx.speeds._default; 7222 } 7223 } 7224 } 7225 7226 // Normalize opt.queue - true/undefined/null -> "fx" 7227 if ( opt.queue == null || opt.queue === true ) { 7228 opt.queue = "fx"; 7229 } 7230 7231 // Queueing 7232 opt.old = opt.complete; 7233 7234 opt.complete = function() { 7235 if ( jQuery.isFunction( opt.old ) ) { 7236 opt.old.call( this ); 7237 } 7238 7239 if ( opt.queue ) { 7240 jQuery.dequeue( this, opt.queue ); 7241 } 7242 }; 7243 7244 return opt; 7245}; 7246 7247jQuery.fn.extend( { 7248 fadeTo: function( speed, to, easing, callback ) { 7249 7250 // Show any hidden elements after setting opacity to 0 7251 return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() 7252 7253 // Animate to the value specified 7254 .end().animate( { opacity: to }, speed, easing, callback ); 7255 }, 7256 animate: function( prop, speed, easing, callback ) { 7257 var empty = jQuery.isEmptyObject( prop ), 7258 optall = jQuery.speed( speed, easing, callback ), 7259 doAnimation = function() { 7260 7261 // Operate on a copy of prop so per-property easing won't be lost 7262 var anim = Animation( this, jQuery.extend( {}, prop ), optall ); 7263 7264 // Empty animations, or finishing resolves immediately 7265 if ( empty || dataPriv.get( this, "finish" ) ) { 7266 anim.stop( true ); 7267 } 7268 }; 7269 doAnimation.finish = doAnimation; 7270 7271 return empty || optall.queue === false ? 7272 this.each( doAnimation ) : 7273 this.queue( optall.queue, doAnimation ); 7274 }, 7275 stop: function( type, clearQueue, gotoEnd ) { 7276 var stopQueue = function( hooks ) { 7277 var stop = hooks.stop; 7278 delete hooks.stop; 7279 stop( gotoEnd ); 7280 }; 7281 7282 if ( typeof type !== "string" ) { 7283 gotoEnd = clearQueue; 7284 clearQueue = type; 7285 type = undefined; 7286 } 7287 if ( clearQueue && type !== false ) { 7288 this.queue( type || "fx", [] ); 7289 } 7290 7291 return this.each( function() { 7292 var dequeue = true, 7293 index = type != null && type + "queueHooks", 7294 timers = jQuery.timers, 7295 data = dataPriv.get( this ); 7296 7297 if ( index ) { 7298 if ( data[ index ] && data[ index ].stop ) { 7299 stopQueue( data[ index ] ); 7300 } 7301 } else { 7302 for ( index in data ) { 7303 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { 7304 stopQueue( data[ index ] ); 7305 } 7306 } 7307 } 7308 7309 for ( index = timers.length; index--; ) { 7310 if ( timers[ index ].elem === this && 7311 ( type == null || timers[ index ].queue === type ) ) { 7312 7313 timers[ index ].anim.stop( gotoEnd ); 7314 dequeue = false;
vendor: 2,199 bytes, lines 7315-7399
7315 timers.splice( index, 1 ); 7316 } 7317 } 7318 7319 // Start the next in the queue if the last step wasn't forced. 7320 // Timers currently will call their complete callbacks, which 7321 // will dequeue but only if they were gotoEnd. 7322 if ( dequeue || !gotoEnd ) { 7323 jQuery.dequeue( this, type ); 7324 } 7325 } ); 7326 }, 7327 finish: function( type ) { 7328 if ( type !== false ) { 7329 type = type || "fx"; 7330 } 7331 return this.each( function() { 7332 var index, 7333 data = dataPriv.get( this ), 7334 queue = data[ type + "queue" ], 7335 hooks = data[ type + "queueHooks" ], 7336 timers = jQuery.timers, 7337 length = queue ? queue.length : 0; 7338 7339 // Enable finishing flag on private data 7340 data.finish = true; 7341 7342 // Empty the queue first 7343 jQuery.queue( this, type, [] ); 7344 7345 if ( hooks && hooks.stop ) { 7346 hooks.stop.call( this, true ); 7347 } 7348 7349 // Look for any active animations, and finish them 7350 for ( index = timers.length; index--; ) { 7351 if ( timers[ index ].elem === this && timers[ index ].queue === type ) { 7352 timers[ index ].anim.stop( true ); 7353 timers.splice( index, 1 ); 7354 } 7355 } 7356 7357 // Look for any animations in the old queue and finish them 7358 for ( index = 0; index < length; index++ ) { 7359 if ( queue[ index ] && queue[ index ].finish ) { 7360 queue[ index ].finish.call( this ); 7361 } 7362 } 7363 7364 // Turn off finishing flag 7365 delete data.finish; 7366 } ); 7367 } 7368} ); 7369 7370jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { 7371 var cssFn = jQuery.fn[ name ]; 7372 jQuery.fn[ name ] = function( speed, easing, callback ) { 7373 return speed == null || typeof speed === "boolean" ? 7374 cssFn.apply( this, arguments ) : 7375 this.animate( genFx( name, true ), speed, easing, callback ); 7376 }; 7377} ); 7378 7379// Generate shortcuts for custom animations 7380jQuery.each( { 7381 slideDown: genFx( "show" ), 7382 slideUp: genFx( "hide" ), 7383 slideToggle: genFx( "toggle" ), 7384 fadeIn: { opacity: "show" }, 7385 fadeOut: { opacity: "hide" }, 7386 fadeToggle: { opacity: "toggle" } 7387}, function( name, props ) { 7388 jQuery.fn[ name ] = function( speed, easing, callback ) { 7389 return this.animate( props, speed, easing, callback ); 7390 }; 7391} ); 7392 7393jQuery.timers = []; 7394jQuery.fx.tick = function() { 7395 var timer, 7396 i = 0, 7397 timers = jQuery.timers; 7398 7399 fxNow = jQuery
vendor: 7,814 bytes, lines 7399-7752
7399.now(); 7400 7401 for ( ; i < timers.length; i++ ) { 7402 timer = timers[ i ]; 7403 7404 // Run the timer and safely remove it when done (allowing for external removal) 7405 if ( !timer() && timers[ i ] === timer ) { 7406 timers.splice( i--, 1 ); 7407 } 7408 } 7409 7410 if ( !timers.length ) { 7411 jQuery.fx.stop(); 7412 } 7413 fxNow = undefined; 7414}; 7415 7416jQuery.fx.timer = function( timer ) { 7417 jQuery.timers.push( timer ); 7418 jQuery.fx.start(); 7419}; 7420 7421jQuery.fx.interval = 13; 7422jQuery.fx.start = function() { 7423 if ( inProgress ) { 7424 return; 7425 } 7426 7427 inProgress = true; 7428 schedule(); 7429}; 7430 7431jQuery.fx.stop = function() { 7432 inProgress = null; 7433}; 7434 7435jQuery.fx.speeds = { 7436 slow: 600, 7437 fast: 200, 7438 7439 // Default speed 7440 _default: 400 7441}; 7442 7443 7444// Based off of the plugin by Clint Helfers, with permission. 7445// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ 7446jQuery.fn.delay = function( time, type ) { 7447 time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; 7448 type = type || "fx"; 7449 7450 return this.queue( type, function( next, hooks ) { 7451 var timeout = window.setTimeout( next, time ); 7452 hooks.stop = function() { 7453 window.clearTimeout( timeout ); 7454 }; 7455 } ); 7456}; 7457 7458 7459( function() { 7460 var input = document.createElement( "input" ), 7461 select = document.createElement( "select" ), 7462 opt = select.appendChild( document.createElement( "option" ) ); 7463 7464 input.type = "checkbox"; 7465 7466 // Support: Android <=4.3 only 7467 // Default value for a checkbox should be "on" 7468 support.checkOn = input.value !== ""; 7469 7470 // Support: IE <=11 only 7471 // Must access selectedIndex to make default options select 7472 support.optSelected = opt.selected; 7473 7474 // Support: IE <=11 only 7475 // An input loses its value after becoming a radio 7476 input = document.createElement( "input" ); 7477 input.value = "t"; 7478 input.type = "radio"; 7479 support.radioValue = input.value === "t"; 7480} )(); 7481 7482 7483var boolHook, 7484 attrHandle = jQuery.expr.attrHandle; 7485 7486jQuery.fn.extend( { 7487 attr: function( name, value ) { 7488 return access( this, jQuery.attr, name, value, arguments.length > 1 ); 7489 }, 7490 7491 removeAttr: function( name ) { 7492 return this.each( function() { 7493 jQuery.removeAttr( this, name ); 7494 } ); 7495 } 7496} ); 7497 7498jQuery.extend( { 7499 attr: function( elem, name, value ) { 7500 var ret, hooks, 7501 nType = elem.nodeType; 7502 7503 // Don't get/set attributes on text, comment and attribute nodes 7504 if ( nType === 3 || nType === 8 || nType === 2 ) { 7505 return; 7506 } 7507 7508 // Fallback to prop when attributes are not supported 7509 if ( typeof elem.getAttribute === "undefined" ) { 7510 return jQuery.prop( elem, name, value ); 7511 } 7512 7513 // Attribute hooks are determined by the lowercase version 7514 // Grab necessary hook if one is defined 7515 if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { 7516 hooks = jQuery.attrHooks[ name.toLowerCase() ] || 7517 ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); 7518 } 7519 7520 if ( value !== undefined ) { 7521 if ( value === null ) { 7522 jQuery.removeAttr( elem, name ); 7523 return; 7524 } 7525 7526 if ( hooks && "set" in hooks && 7527 ( ret = hooks.set( elem, value, name ) ) !== undefined ) { 7528 return ret; 7529 } 7530 7531 elem.setAttribute( name, value + "" ); 7532 return value; 7533 } 7534 7535 if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { 7536 return ret; 7537 } 7538 7539 ret = jQuery.find.attr( elem, name ); 7540 7541 // Non-existent attributes return null, we normalize to undefined 7542 return ret == null ? undefined : ret; 7543 }, 7544 7545 attrHooks: { 7546 type: { 7547 set: function( elem, value ) { 7548 if ( !support.radioValue && value === "radio" && 7549 nodeName( elem, "input" ) ) { 7550 var val = elem.value; 7551 elem.setAttribute( "type", value ); 7552 if ( val ) { 7553 elem.value = val; 7554 } 7555 return value; 7556 } 7557 } 7558 } 7559 }, 7560 7561 removeAttr: function( elem, value ) { 7562 var name, 7563 i = 0, 7564 7565 // Attribute names can contain non-HTML whitespace characters 7566 // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 7567 attrNames = value && value.match( rnothtmlwhite ); 7568 7569 if ( attrNames && elem.nodeType === 1 ) { 7570 while ( ( name = attrNames[ i++ ] ) ) { 7571 elem.removeAttribute( name ); 7572 } 7573 } 7574 } 7575} ); 7576 7577// Hooks for boolean attributes 7578boolHook = { 7579 set: function( elem, value, name ) { 7580 if ( value === false ) { 7581 7582 // Remove boolean attributes when set to false 7583 jQuery.removeAttr( elem, name ); 7584 } else { 7585 elem.setAttribute( name, name ); 7586 } 7587 return name; 7588 } 7589}; 7590 7591jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { 7592 var getter = attrHandle[ name ] || jQuery.find.attr; 7593 7594 attrHandle[ name ] = function( elem, name, isXML ) { 7595 var ret, handle, 7596 lowercaseName = name.toLowerCase(); 7597 7598 if ( !isXML ) { 7599 7600 // Avoid an infinite loop by temporarily removing this function from the getter 7601 handle = attrHandle[ lowercaseName ]; 7602 attrHandle[ lowercaseName ] = ret; 7603 ret = getter( elem, name, isXML ) != null ? 7604 lowercaseName : 7605 null; 7606 attrHandle[ lowercaseName ] = handle; 7607 } 7608 return ret; 7609 }; 7610} ); 7611 7612 7613 7614 7615var rfocusable = /^(?:input|select|textarea|button)$/i, 7616 rclickable = /^(?:a|area)$/i; 7617 7618jQuery.fn.extend( { 7619 prop: function( name, value ) { 7620 return access( this, jQuery.prop, name, value, arguments.length > 1 ); 7621 }, 7622 7623 removeProp: function( name ) { 7624 return this.each( function() { 7625 delete this[ jQuery.propFix[ name ] || name ]; 7626 } ); 7627 } 7628} ); 7629 7630jQuery.extend( { 7631 prop: function( elem, name, value ) { 7632 var ret, hooks, 7633 nType = elem.nodeType; 7634 7635 // Don't get/set properties on text, comment and attribute nodes 7636 if ( nType === 3 || nType === 8 || nType === 2 ) { 7637 return; 7638 } 7639 7640 if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { 7641 7642 // Fix name and attach hooks 7643 name = jQuery.propFix[ name ] || name; 7644 hooks = jQuery.propHooks[ name ]; 7645 } 7646 7647 if ( value !== undefined ) { 7648 if ( hooks && "set" in hooks && 7649 ( ret = hooks.set( elem, value, name ) ) !== undefined ) { 7650 return ret; 7651 } 7652 7653 return ( elem[ name ] = value ); 7654 } 7655 7656 if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { 7657 return ret; 7658 } 7659 7660 return elem[ name ]; 7661 }, 7662 7663 propHooks: { 7664 tabIndex: { 7665 get: function( elem ) { 7666 7667 // Support: IE <=9 - 11 only 7668 // elem.tabIndex doesn't always return the 7669 // correct value when it hasn't been explicitly set 7670 // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ 7671 // Use proper attribute retrieval(#12072) 7672 var tabindex = jQuery.find.attr( elem, "tabindex" ); 7673 7674 if ( tabindex ) { 7675 return parseInt( tabindex, 10 ); 7676 } 7677 7678 if ( 7679 rfocusable.test( elem.nodeName ) || 7680 rclickable.test( elem.nodeName ) && 7681 elem.href 7682 ) { 7683 return 0; 7684 } 7685 7686 return -1; 7687 } 7688 } 7689 }, 7690 7691 propFix: { 7692 "for": "htmlFor", 7693 "class": "className" 7694 } 7695} ); 7696 7697// Support: IE <=11 only 7698// Accessing the selectedIndex property 7699// forces the browser to respect setting selected 7700// on the option 7701// The getter ensures a default option is selected 7702// when in an optgroup 7703// eslint rule "no-unused-expressions" is disabled for this code 7704// since it considers such accessions noop 7705if ( !support.optSelected ) { 7706 jQuery.propHooks.selected = { 7707 get: function( elem ) { 7708 7709 /* eslint no-unused-expressions: "off" */ 7710 7711 var parent = elem.parentNode; 7712 if ( parent && parent.parentNode ) { 7713 parent.parentNode.selectedIndex; 7714 } 7715 return null; 7716 }, 7717 set: function( elem ) { 7718 7719 /* eslint no-unused-expressions: "off" */ 7720 7721 var parent = elem.parentNode; 7722 if ( parent ) { 7723 parent.selectedIndex; 7724 7725 if ( parent.parentNode ) { 7726 parent.parentNode.selectedIndex; 7727 } 7728 } 7729 } 7730 }; 7731} 7732 7733jQuery.each( [ 7734 "tabIndex", 7735 "readOnly", 7736 "maxLength", 7737 "cellSpacing", 7738 "cellPadding", 7739 "rowSpan", 7740 "colSpan", 7741 "useMap", 7742 "frameBorder", 7743 "contentEditable" 7744], function() { 7745 jQuery.propFix[ this.toLowerCase() ] = this; 7746} ); 7747 7748 7749 7750 7751 // Strip and collapse whitespace according to HTML spec 7752 // https://
vendor: 4,260 bytes, lines 7752-7918
7752html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace 7753 function stripAndCollapse( value ) { 7754 var tokens = value.match( rnothtmlwhite ) || []; 7755 return tokens.join( " " ); 7756 } 7757 7758 7759function getClass( elem ) { 7760 return elem.getAttribute && elem.getAttribute( "class" ) || ""; 7761} 7762 7763jQuery.fn.extend( { 7764 addClass: function( value ) { 7765 var classes, elem, cur, curValue, clazz, j, finalValue, 7766 i = 0; 7767 7768 if ( jQuery.isFunction( value ) ) { 7769 return this.each( function( j ) { 7770 jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); 7771 } ); 7772 } 7773 7774 if ( typeof value === "string" && value ) { 7775 classes = value.match( rnothtmlwhite ) || []; 7776 7777 while ( ( elem = this[ i++ ] ) ) { 7778 curValue = getClass( elem ); 7779 cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); 7780 7781 if ( cur ) { 7782 j = 0; 7783 while ( ( clazz = classes[ j++ ] ) ) { 7784 if ( cur.indexOf( " " + clazz + " " ) < 0 ) { 7785 cur += clazz + " "; 7786 } 7787 } 7788 7789 // Only assign if different to avoid unneeded rendering. 7790 finalValue = stripAndCollapse( cur ); 7791 if ( curValue !== finalValue ) { 7792 elem.setAttribute( "class", finalValue ); 7793 } 7794 } 7795 } 7796 } 7797 7798 return this; 7799 }, 7800 7801 removeClass: function( value ) { 7802 var classes, elem, cur, curValue, clazz, j, finalValue, 7803 i = 0; 7804 7805 if ( jQuery.isFunction( value ) ) { 7806 return this.each( function( j ) { 7807 jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); 7808 } ); 7809 } 7810 7811 if ( !arguments.length ) { 7812 return this.attr( "class", "" ); 7813 } 7814 7815 if ( typeof value === "string" && value ) { 7816 classes = value.match( rnothtmlwhite ) || []; 7817 7818 while ( ( elem = this[ i++ ] ) ) { 7819 curValue = getClass( elem ); 7820 7821 // This expression is here for better compressibility (see addClass) 7822 cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); 7823 7824 if ( cur ) { 7825 j = 0; 7826 while ( ( clazz = classes[ j++ ] ) ) { 7827 7828 // Remove *all* instances 7829 while ( cur.indexOf( " " + clazz + " " ) > -1 ) { 7830 cur = cur.replace( " " + clazz + " ", " " ); 7831 } 7832 } 7833 7834 // Only assign if different to avoid unneeded rendering. 7835 finalValue = stripAndCollapse( cur ); 7836 if ( curValue !== finalValue ) { 7837 elem.setAttribute( "class", finalValue ); 7838 } 7839 } 7840 } 7841 } 7842 7843 return this; 7844 }, 7845 7846 toggleClass: function( value, stateVal ) { 7847 var type = typeof value; 7848 7849 if ( typeof stateVal === "boolean" && type === "string" ) { 7850 return stateVal ? this.addClass( value ) : this.removeClass( value ); 7851 } 7852 7853 if ( jQuery.isFunction( value ) ) { 7854 return this.each( function( i ) { 7855 jQuery( this ).toggleClass( 7856 value.call( this, i, getClass( this ), stateVal ), 7857 stateVal 7858 ); 7859 } ); 7860 } 7861 7862 return this.each( function() { 7863 var className, i, self, classNames; 7864 7865 if ( type === "string" ) { 7866 7867 // Toggle individual class names 7868 i = 0; 7869 self = jQuery( this ); 7870 classNames = value.match( rnothtmlwhite ) || []; 7871 7872 while ( ( className = classNames[ i++ ] ) ) { 7873 7874 // Check each className given, space separated list 7875 if ( self.hasClass( className ) ) { 7876 self.removeClass( className ); 7877 } else { 7878 self.addClass( className ); 7879 } 7880 } 7881 7882 // Toggle whole class name 7883 } else if ( value === undefined || type === "boolean" ) { 7884 className = getClass( this ); 7885 if ( className ) { 7886 7887 // Store className if set 7888 dataPriv.set( this, "__className__", className ); 7889 } 7890 7891 // If the element has a class name or if we're passed `false`, 7892 // then remove the whole classname (if there was one, the above saved it). 7893 // Otherwise bring back whatever was previously saved (if anything), 7894 // falling back to the empty string if nothing was stored. 7895 if ( this.setAttribute ) { 7896 this.setAttribute( "class", 7897 className || value === false ? 7898 "" : 7899 dataPriv.get( this, "__className__" ) || "" 7900 ); 7901 } 7902 } 7903 } ); 7904 }, 7905 7906 hasClass: function( selector ) { 7907 var className, elem, 7908 i = 0; 7909 7910 className = " " + selector + " "; 7911 while ( ( elem = this[ i++ ] ) ) { 7912 if ( elem.nodeType === 1 && 7913 ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { 7914 return true; 7915 } 7916 } 7917 7918 return false;
vendor: 1,377 bytes, lines 7919-7988
7919 } 7920} ); 7921 7922 7923 7924 7925var rreturn = /\r/g; 7926 7927jQuery.fn.extend( { 7928 val: function( value ) { 7929 var hooks, ret, isFunction, 7930 elem = this[ 0 ]; 7931 7932 if ( !arguments.length ) { 7933 if ( elem ) { 7934 hooks = jQuery.valHooks[ elem.type ] || 7935 jQuery.valHooks[ elem.nodeName.toLowerCase() ]; 7936 7937 if ( hooks && 7938 "get" in hooks && 7939 ( ret = hooks.get( elem, "value" ) ) !== undefined 7940 ) { 7941 return ret; 7942 } 7943 7944 ret = elem.value; 7945 7946 // Handle most common string cases 7947 if ( typeof ret === "string" ) { 7948 return ret.replace( rreturn, "" ); 7949 } 7950 7951 // Handle cases where value is null/undef or number 7952 return ret == null ? "" : ret; 7953 } 7954 7955 return; 7956 } 7957 7958 isFunction = jQuery.isFunction( value ); 7959 7960 return this.each( function( i ) { 7961 var val; 7962 7963 if ( this.nodeType !== 1 ) { 7964 return; 7965 } 7966 7967 if ( isFunction ) { 7968 val = value.call( this, i, jQuery( this ).val() ); 7969 } else { 7970 val = value; 7971 } 7972 7973 // Treat null/undefined as ""; convert numbers to string 7974 if ( val == null ) { 7975 val = ""; 7976 7977 } else if ( typeof val === "number" ) { 7978 val += ""; 7979 7980 } else if ( Array.isArray( val ) ) { 7981 val = jQuery.map( val, function( value ) { 7982 return value == null ? "" : value + ""; 7983 } ); 7984 } 7985 7986 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; 7987 7988 // If set returns undefined, fall back to normal setting
vendor: 8,160 bytes, lines 7989-8307
7989 if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { 7990 this.value = val; 7991 } 7992 } ); 7993 } 7994} ); 7995 7996jQuery.extend( { 7997 valHooks: { 7998 option: { 7999 get: function( elem ) { 8000 8001 var val = jQuery.find.attr( elem, "value" ); 8002 return val != null ? 8003 val : 8004 8005 // Support: IE <=10 - 11 only 8006 // option.text throws exceptions (#14686, #14858) 8007 // Strip and collapse whitespace 8008 // https://html.spec.whatwg.org/#strip-and-collapse-whitespace 8009 stripAndCollapse( jQuery.text( elem ) ); 8010 } 8011 }, 8012 select: { 8013 get: function( elem ) { 8014 var value, option, i, 8015 options = elem.options, 8016 index = elem.selectedIndex, 8017 one = elem.type === "select-one", 8018 values = one ? null : [], 8019 max = one ? index + 1 : options.length; 8020 8021 if ( index < 0 ) { 8022 i = max; 8023 8024 } else { 8025 i = one ? index : 0; 8026 } 8027 8028 // Loop through all the selected options 8029 for ( ; i < max; i++ ) { 8030 option = options[ i ]; 8031 8032 // Support: IE <=9 only 8033 // IE8-9 doesn't update selected after form reset (#2551) 8034 if ( ( option.selected || i === index ) && 8035 8036 // Don't return options that are disabled or in a disabled optgroup 8037 !option.disabled && 8038 ( !option.parentNode.disabled || 8039 !nodeName( option.parentNode, "optgroup" ) ) ) { 8040 8041 // Get the specific value for the option 8042 value = jQuery( option ).val(); 8043 8044 // We don't need an array for one selects 8045 if ( one ) { 8046 return value; 8047 } 8048 8049 // Multi-Selects return an array 8050 values.push( value ); 8051 } 8052 } 8053 8054 return values; 8055 }, 8056 8057 set: function( elem, value ) { 8058 var optionSet, option, 8059 options = elem.options, 8060 values = jQuery.makeArray( value ), 8061 i = options.length; 8062 8063 while ( i-- ) { 8064 option = options[ i ]; 8065 8066 /* eslint-disable no-cond-assign */ 8067 8068 if ( option.selected = 8069 jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 8070 ) { 8071 optionSet = true; 8072 } 8073 8074 /* eslint-enable no-cond-assign */ 8075 } 8076 8077 // Force browsers to behave consistently when non-matching value is set 8078 if ( !optionSet ) { 8079 elem.selectedIndex = -1; 8080 } 8081 return values; 8082 } 8083 } 8084 } 8085} ); 8086 8087// Radios and checkboxes getter/setter 8088jQuery.each( [ "radio", "checkbox" ], function() { 8089 jQuery.valHooks[ this ] = { 8090 set: function( elem, value ) { 8091 if ( Array.isArray( value ) ) { 8092 return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); 8093 } 8094 } 8095 }; 8096 if ( !support.checkOn ) { 8097 jQuery.valHooks[ this ].get = function( elem ) { 8098 return elem.getAttribute( "value" ) === null ? "on" : elem.value; 8099 }; 8100 } 8101} ); 8102 8103 8104 8105 8106// Return jQuery for attributes-only inclusion 8107 8108 8109var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; 8110 8111jQuery.extend( jQuery.event, { 8112 8113 trigger: function( event, data, elem, onlyHandlers ) { 8114 8115 var i, cur, tmp, bubbleType, ontype, handle, special, 8116 eventPath = [ elem || document ], 8117 type = hasOwn.call( event, "type" ) ? event.type : event, 8118 namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; 8119 8120 cur = tmp = elem = elem || document; 8121 8122 // Don't do events on text and comment nodes 8123 if ( elem.nodeType === 3 || elem.nodeType === 8 ) { 8124 return; 8125 } 8126 8127 // focus/blur morphs to focusin/out; ensure we're not firing them right now 8128 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { 8129 return; 8130 } 8131 8132 if ( type.indexOf( "." ) > -1 ) { 8133 8134 // Namespaced trigger; create a regexp to match event type in handle() 8135 namespaces = type.split( "." ); 8136 type = namespaces.shift(); 8137 namespaces.sort(); 8138 } 8139 ontype = type.indexOf( ":" ) < 0 && "on" + type; 8140 8141 // Caller can pass in a jQuery.Event object, Object, or just an event type string 8142 event = event[ jQuery.expando ] ? 8143 event : 8144 new jQuery.Event( type, typeof event === "object" && event ); 8145 8146 // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) 8147 event.isTrigger = onlyHandlers ? 2 : 3; 8148 event.namespace = namespaces.join( "." ); 8149 event.rnamespace = event.namespace ? 8150 new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : 8151 null; 8152 8153 // Clean up the event in case it is being reused 8154 event.result = undefined; 8155 if ( !event.target ) { 8156 event.target = elem; 8157 } 8158 8159 // Clone any incoming data and prepend the event, creating the handler arg list 8160 data = data == null ? 8161 [ event ] : 8162 jQuery.makeArray( data, [ event ] ); 8163 8164 // Allow special events to draw outside the lines 8165 special = jQuery.event.special[ type ] || {}; 8166 if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { 8167 return; 8168 } 8169 8170 // Determine event propagation path in advance, per W3C events spec (#9951) 8171 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) 8172 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { 8173 8174 bubbleType = special.delegateType || type; 8175 if ( !rfocusMorph.test( bubbleType + type ) ) { 8176 cur = cur.parentNode; 8177 } 8178 for ( ; cur; cur = cur.parentNode ) { 8179 eventPath.push( cur ); 8180 tmp = cur; 8181 } 8182 8183 // Only add window if we got to document (e.g., not plain obj or detached DOM) 8184 if ( tmp === ( elem.ownerDocument || document ) ) { 8185 eventPath.push( tmp.defaultView || tmp.parentWindow || window ); 8186 } 8187 } 8188 8189 // Fire handlers on the event path 8190 i = 0; 8191 while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { 8192 8193 event.type = i > 1 ? 8194 bubbleType : 8195 special.bindType || type; 8196 8197 // jQuery handler 8198 handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && 8199 dataPriv.get( cur, "handle" ); 8200 if ( handle ) { 8201 handle.apply( cur, data ); 8202 } 8203 8204 // Native handler 8205 handle = ontype && cur[ ontype ]; 8206 if ( handle && handle.apply && acceptData( cur ) ) { 8207 event.result = handle.apply( cur, data ); 8208 if ( event.result === false ) { 8209 event.preventDefault(); 8210 } 8211 } 8212 } 8213 event.type = type; 8214 8215 // If nobody prevented the default action, do it now 8216 if ( !onlyHandlers && !event.isDefaultPrevented() ) { 8217 8218 if ( ( !special._default || 8219 special._default.apply( eventPath.pop(), data ) === false ) && 8220 acceptData( elem ) ) { 8221 8222 // Call a native DOM method on the target with the same name as the event. 8223 // Don't do default actions on window, that's where global variables be (#6170) 8224 if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { 8225 8226 // Don't re-trigger an onFOO event when we call its FOO() method 8227 tmp = elem[ ontype ]; 8228 8229 if ( tmp ) { 8230 elem[ ontype ] = null; 8231 } 8232 8233 // Prevent re-triggering of the same event, since we already bubbled it above 8234 jQuery.event.triggered = type; 8235 elem[ type ](); 8236 jQuery.event.triggered = undefined; 8237 8238 if ( tmp ) { 8239 elem[ ontype ] = tmp; 8240 } 8241 } 8242 } 8243 } 8244 8245 return event.result; 8246 }, 8247 8248 // Piggyback on a donor event to simulate a different one 8249 // Used only for `focus(in | out)` events 8250 simulate: function( type, elem, event ) { 8251 var e = jQuery.extend( 8252 new jQuery.Event(), 8253 event, 8254 { 8255 type: type, 8256 isSimulated: true 8257 } 8258 ); 8259 8260 jQuery.event.trigger( e, null, elem ); 8261 } 8262 8263} ); 8264 8265jQuery.fn.extend( { 8266 8267 trigger: function( type, data ) { 8268 return this.each( function() { 8269 jQuery.event.trigger( type, data, this ); 8270 } ); 8271 }, 8272 triggerHandler: function( type, data ) { 8273 var elem = this[ 0 ]; 8274 if ( elem ) { 8275 return jQuery.event.trigger( type, data, elem, true ); 8276 } 8277 } 8278} ); 8279 8280 8281jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + 8282 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + 8283 "change select submit keydown keypress keyup contextmenu" ).split( " " ), 8284 function( i, name ) { 8285 8286 // Handle event binding 8287 jQuery.fn[ name ] = function( data, fn ) { 8288 return arguments.length > 0 ? 8289 this.on( name, null, data, fn ) : 8290 this.trigger( name ); 8291 }; 8292} ); 8293 8294jQuery.fn.extend( { 8295 hover: function( fnOver, fnOut ) { 8296 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); 8297 } 8298} ); 8299 8300 8301 8302 8303support.focusin = "onfocusin" in window; 8304 8305 8306// Support: Firefox <=44 8307// Firefox doesn't have focus(in | out) events
vendor: 4,174 bytes, lines 8308-8471
8308// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 8309// 8310// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 8311// focus(in | out) events fire after focus & blur events, 8312// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order 8313// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 8314if ( !support.focusin ) { 8315 jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { 8316 8317 // Attach a single capturing handler on the document while someone wants focusin/focusout 8318 var handler = function( event ) { 8319 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); 8320 }; 8321 8322 jQuery.event.special[ fix ] = { 8323 setup: function() { 8324 var doc = this.ownerDocument || this, 8325 attaches = dataPriv.access( doc, fix ); 8326 8327 if ( !attaches ) { 8328 doc.addEventListener( orig, handler, true ); 8329 } 8330 dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); 8331 }, 8332 teardown: function() { 8333 var doc = this.ownerDocument || this, 8334 attaches = dataPriv.access( doc, fix ) - 1; 8335 8336 if ( !attaches ) { 8337 doc.removeEventListener( orig, handler, true ); 8338 dataPriv.remove( doc, fix ); 8339 8340 } else { 8341 dataPriv.access( doc, fix, attaches ); 8342 } 8343 } 8344 }; 8345 } ); 8346} 8347var location = window.location; 8348 8349var nonce = jQuery.now(); 8350 8351var rquery = ( /\?/ ); 8352 8353 8354 8355// Cross-browser xml parsing 8356jQuery.parseXML = function( data ) { 8357 var xml; 8358 if ( !data || typeof data !== "string" ) { 8359 return null; 8360 } 8361 8362 // Support: IE 9 - 11 only 8363 // IE throws on parseFromString with invalid input. 8364 try { 8365 xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); 8366 } catch ( e ) { 8367 xml = undefined; 8368 } 8369 8370 if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { 8371 jQuery.error( "Invalid XML: " + data ); 8372 } 8373 return xml; 8374}; 8375 8376 8377var 8378 rbracket = /\[\]$/, 8379 rCRLF = /\r?\n/g, 8380 rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, 8381 rsubmittable = /^(?:input|select|textarea|keygen)/i; 8382 8383function buildParams( prefix, obj, traditional, add ) { 8384 var name; 8385 8386 if ( Array.isArray( obj ) ) { 8387 8388 // Serialize array item. 8389 jQuery.each( obj, function( i, v ) { 8390 if ( traditional || rbracket.test( prefix ) ) { 8391 8392 // Treat each array item as a scalar. 8393 add( prefix, v ); 8394 8395 } else { 8396 8397 // Item is non-scalar (array or object), encode its numeric index. 8398 buildParams( 8399 prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", 8400 v, 8401 traditional, 8402 add 8403 ); 8404 } 8405 } ); 8406 8407 } else if ( !traditional && jQuery.type( obj ) === "object" ) { 8408 8409 // Serialize object item. 8410 for ( name in obj ) { 8411 buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); 8412 } 8413 8414 } else { 8415 8416 // Serialize scalar item. 8417 add( prefix, obj ); 8418 } 8419} 8420 8421// Serialize an array of form elements or a set of 8422// key/values into a query string 8423jQuery.param = function( a, traditional ) { 8424 var prefix, 8425 s = [], 8426 add = function( key, valueOrFunction ) { 8427 8428 // If value is a function, invoke it and use its return value 8429 var value = jQuery.isFunction( valueOrFunction ) ? 8430 valueOrFunction() : 8431 valueOrFunction; 8432 8433 s[ s.length ] = encodeURIComponent( key ) + "=" + 8434 encodeURIComponent( value == null ? "" : value ); 8435 }; 8436 8437 // If an array was passed in, assume that it is an array of form elements. 8438 if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { 8439 8440 // Serialize the form elements 8441 jQuery.each( a, function() { 8442 add( this.name, this.value ); 8443 } ); 8444 8445 } else { 8446 8447 // If traditional, encode the "old" way (the way 1.3.2 or older 8448 // did it), otherwise encode params recursively. 8449 for ( prefix in a ) { 8450 buildParams( prefix, a[ prefix ], traditional, add ); 8451 } 8452 } 8453 8454 // Return the resulting serialization 8455 return s.join( "&" ); 8456}; 8457 8458jQuery.fn.extend( { 8459 serialize: function() { 8460 return jQuery.param( this.serializeArray() ); 8461 }, 8462 serializeArray: function() { 8463 return this.map( function() { 8464 8465 // Can add propHook for "elements" to filter or add form elements 8466 var elements = jQuery.prop( this, "elements" ); 8467 return elements ? jQuery.makeArray( elements ) : this; 8468 } ) 8469 .filter( function() { 8470 var type = this.type; 8471
vendor: 4,939 bytes, lines 8472-8644
8472 // Use .is( ":disabled" ) so that fieldset[disabled] works 8473 return this.name && !jQuery( this ).is( ":disabled" ) && 8474 rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && 8475 ( this.checked || !rcheckableType.test( type ) ); 8476 } ) 8477 .map( function( i, elem ) { 8478 var val = jQuery( this ).val(); 8479 8480 if ( val == null ) { 8481 return null; 8482 } 8483 8484 if ( Array.isArray( val ) ) { 8485 return jQuery.map( val, function( val ) { 8486 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; 8487 } ); 8488 } 8489 8490 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; 8491 } ).get(); 8492 } 8493} ); 8494 8495 8496var 8497 r20 = /%20/g, 8498 rhash = /#.*$/, 8499 rantiCache = /([?&])_=[^&]*/, 8500 rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, 8501 8502 // #7653, #8125, #8152: local protocol detection 8503 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, 8504 rnoContent = /^(?:GET|HEAD)$/, 8505 rprotocol = /^\/\//, 8506 8507 /* Prefilters 8508 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) 8509 * 2) These are called: 8510 * - BEFORE asking for a transport 8511 * - AFTER param serialization (s.data is a string if s.processData is true) 8512 * 3) key is the dataType 8513 * 4) the catchall symbol "*" can be used 8514 * 5) execution will start with transport dataType and THEN continue down to "*" if needed 8515 */ 8516 prefilters = {}, 8517 8518 /* Transports bindings 8519 * 1) key is the dataType 8520 * 2) the catchall symbol "*" can be used 8521 * 3) selection will start with transport dataType and THEN go to "*" if needed 8522 */ 8523 transports = {}, 8524 8525 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression 8526 allTypes = "*/".concat( "*" ), 8527 8528 // Anchor tag for parsing the document origin 8529 originAnchor = document.createElement( "a" ); 8530 originAnchor.href = location.href; 8531 8532// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport 8533function addToPrefiltersOrTransports( structure ) { 8534 8535 // dataTypeExpression is optional and defaults to "*" 8536 return function( dataTypeExpression, func ) { 8537 8538 if ( typeof dataTypeExpression !== "string" ) { 8539 func = dataTypeExpression; 8540 dataTypeExpression = "*"; 8541 } 8542 8543 var dataType, 8544 i = 0, 8545 dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; 8546 8547 if ( jQuery.isFunction( func ) ) { 8548 8549 // For each dataType in the dataTypeExpression 8550 while ( ( dataType = dataTypes[ i++ ] ) ) { 8551 8552 // Prepend if requested 8553 if ( dataType[ 0 ] === "+" ) { 8554 dataType = dataType.slice( 1 ) || "*"; 8555 ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); 8556 8557 // Otherwise append 8558 } else { 8559 ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); 8560 } 8561 } 8562 } 8563 }; 8564} 8565 8566// Base inspection function for prefilters and transports 8567function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { 8568 8569 var inspected = {}, 8570 seekingTransport = ( structure === transports ); 8571 8572 function inspect( dataType ) { 8573 var selected; 8574 inspected[ dataType ] = true; 8575 jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { 8576 var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); 8577 if ( typeof dataTypeOrTransport === "string" && 8578 !seekingTransport && !inspected[ dataTypeOrTransport ] ) { 8579 8580 options.dataTypes.unshift( dataTypeOrTransport ); 8581 inspect( dataTypeOrTransport ); 8582 return false; 8583 } else if ( seekingTransport ) { 8584 return !( selected = dataTypeOrTransport ); 8585 } 8586 } ); 8587 return selected; 8588 } 8589 8590 return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); 8591} 8592 8593// A special extend for ajax options 8594// that takes "flat" options (not to be deep extended) 8595// Fixes #9887 8596function ajaxExtend( target, src ) { 8597 var key, deep, 8598 flatOptions = jQuery.ajaxSettings.flatOptions || {}; 8599 8600 for ( key in src ) { 8601 if ( src[ key ] !== undefined ) { 8602 ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; 8603 } 8604 } 8605 if ( deep ) { 8606 jQuery.extend( true, target, deep ); 8607 } 8608 8609 return target; 8610} 8611 8612/* Handles responses to an ajax request: 8613 * - finds the right dataType (mediates between content-type and expected dataType) 8614 * - returns the corresponding response 8615 */ 8616function ajaxHandleResponses( s, jqXHR, responses ) { 8617 8618 var ct, type, finalDataType, firstDataType, 8619 contents = s.contents, 8620 dataTypes = s.dataTypes; 8621 8622 // Remove auto dataType and get content-type in the process 8623 while ( dataTypes[ 0 ] === "*" ) { 8624 dataTypes.shift(); 8625 if ( ct === undefined ) { 8626 ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); 8627 } 8628 } 8629 8630 // Check if we're dealing with a known content-type 8631 if ( ct ) { 8632 for ( type in contents ) { 8633 if ( contents[ type ] && contents[ type ].test( ct ) ) { 8634 dataTypes.unshift( type ); 8635 break; 8636 } 8637 } 8638 } 8639 8640 // Check to see if we have a response for the expected dataType 8641 if ( dataTypes[ 0 ] in responses ) { 8642 finalDataType = dataTypes[ 0 ]; 8643 } else { 8644
vendor: 18,932 bytes, lines 8645-9402
8645 // Try convertible dataTypes 8646 for ( type in responses ) { 8647 if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { 8648 finalDataType = type; 8649 break; 8650 } 8651 if ( !firstDataType ) { 8652 firstDataType = type; 8653 } 8654 } 8655 8656 // Or just use first one 8657 finalDataType = finalDataType || firstDataType; 8658 } 8659 8660 // If we found a dataType 8661 // We add the dataType to the list if needed 8662 // and return the corresponding response 8663 if ( finalDataType ) { 8664 if ( finalDataType !== dataTypes[ 0 ] ) { 8665 dataTypes.unshift( finalDataType ); 8666 } 8667 return responses[ finalDataType ]; 8668 } 8669} 8670 8671/* Chain conversions given the request and the original response 8672 * Also sets the responseXXX fields on the jqXHR instance 8673 */ 8674function ajaxConvert( s, response, jqXHR, isSuccess ) { 8675 var conv2, current, conv, tmp, prev, 8676 converters = {}, 8677 8678 // Work with a copy of dataTypes in case we need to modify it for conversion 8679 dataTypes = s.dataTypes.slice(); 8680 8681 // Create converters map with lowercased keys 8682 if ( dataTypes[ 1 ] ) { 8683 for ( conv in s.converters ) { 8684 converters[ conv.toLowerCase() ] = s.converters[ conv ]; 8685 } 8686 } 8687 8688 current = dataTypes.shift(); 8689 8690 // Convert to each sequential dataType 8691 while ( current ) { 8692 8693 if ( s.responseFields[ current ] ) { 8694 jqXHR[ s.responseFields[ current ] ] = response; 8695 } 8696 8697 // Apply the dataFilter if provided 8698 if ( !prev && isSuccess && s.dataFilter ) { 8699 response = s.dataFilter( response, s.dataType ); 8700 } 8701 8702 prev = current; 8703 current = dataTypes.shift(); 8704 8705 if ( current ) { 8706 8707 // There's only work to do if current dataType is non-auto 8708 if ( current === "*" ) { 8709 8710 current = prev; 8711 8712 // Convert response if prev dataType is non-auto and differs from current 8713 } else if ( prev !== "*" && prev !== current ) { 8714 8715 // Seek a direct converter 8716 conv = converters[ prev + " " + current ] || converters[ "* " + current ]; 8717 8718 // If none found, seek a pair 8719 if ( !conv ) { 8720 for ( conv2 in converters ) { 8721 8722 // If conv2 outputs current 8723 tmp = conv2.split( " " ); 8724 if ( tmp[ 1 ] === current ) { 8725 8726 // If prev can be converted to accepted input 8727 conv = converters[ prev + " " + tmp[ 0 ] ] || 8728 converters[ "* " + tmp[ 0 ] ]; 8729 if ( conv ) { 8730 8731 // Condense equivalence converters 8732 if ( conv === true ) { 8733 conv = converters[ conv2 ]; 8734 8735 // Otherwise, insert the intermediate dataType 8736 } else if ( converters[ conv2 ] !== true ) { 8737 current = tmp[ 0 ]; 8738 dataTypes.unshift( tmp[ 1 ] ); 8739 } 8740 break; 8741 } 8742 } 8743 } 8744 } 8745 8746 // Apply converter (if not an equivalence) 8747 if ( conv !== true ) { 8748 8749 // Unless errors are allowed to bubble, catch and return them 8750 if ( conv && s.throws ) { 8751 response = conv( response ); 8752 } else { 8753 try { 8754 response = conv( response ); 8755 } catch ( e ) { 8756 return { 8757 state: "parsererror", 8758 error: conv ? e : "No conversion from " + prev + " to " + current 8759 }; 8760 } 8761 } 8762 } 8763 } 8764 } 8765 } 8766 8767 return { state: "success", data: response }; 8768} 8769 8770jQuery.extend( { 8771 8772 // Counter for holding the number of active queries 8773 active: 0, 8774 8775 // Last-Modified header cache for next request 8776 lastModified: {}, 8777 etag: {}, 8778 8779 ajaxSettings: { 8780 url: location.href, 8781 type: "GET", 8782 isLocal: rlocalProtocol.test( location.protocol ), 8783 global: true, 8784 processData: true, 8785 async: true, 8786 contentType: "application/x-www-form-urlencoded; charset=UTF-8", 8787 8788 /* 8789 timeout: 0, 8790 data: null, 8791 dataType: null, 8792 username: null, 8793 password: null, 8794 cache: null, 8795 throws: false, 8796 traditional: false, 8797 headers: {}, 8798 */ 8799 8800 accepts: { 8801 "*": allTypes, 8802 text: "text/plain", 8803 html: "text/html", 8804 xml: "application/xml, text/xml", 8805 json: "application/json, text/javascript" 8806 }, 8807 8808 contents: { 8809 xml: /\bxml\b/, 8810 html: /\bhtml/, 8811 json: /\bjson\b/ 8812 }, 8813 8814 responseFields: { 8815 xml: "responseXML", 8816 text: "responseText", 8817 json: "responseJSON" 8818 }, 8819 8820 // Data converters 8821 // Keys separate source (or catchall "*") and destination types with a single space 8822 converters: { 8823 8824 // Convert anything to text 8825 "* text": String, 8826 8827 // Text to html (true = no transformation) 8828 "text html": true, 8829 8830 // Evaluate text as a json expression 8831 "text json": JSON.parse, 8832 8833 // Parse text as xml 8834 "text xml": jQuery.parseXML 8835 }, 8836 8837 // For options that shouldn't be deep extended: 8838 // you can add your own custom options here if 8839 // and when you create one that shouldn't be 8840 // deep extended (see ajaxExtend) 8841 flatOptions: { 8842 url: true, 8843 context: true 8844 } 8845 }, 8846 8847 // Creates a full fledged settings object into target 8848 // with both ajaxSettings and settings fields. 8849 // If target is omitted, writes into ajaxSettings. 8850 ajaxSetup: function( target, settings ) { 8851 return settings ? 8852 8853 // Building a settings object 8854 ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : 8855 8856 // Extending ajaxSettings 8857 ajaxExtend( jQuery.ajaxSettings, target ); 8858 }, 8859 8860 ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), 8861 ajaxTransport: addToPrefiltersOrTransports( transports ), 8862 8863 // Main method 8864 ajax: function( url, options ) { 8865 8866 // If url is an object, simulate pre-1.5 signature 8867 if ( typeof url === "object" ) { 8868 options = url; 8869 url = undefined; 8870 } 8871 8872 // Force options to be an object 8873 options = options || {}; 8874 8875 var transport, 8876 8877 // URL without anti-cache param 8878 cacheURL, 8879 8880 // Response headers 8881 responseHeadersString, 8882 responseHeaders, 8883 8884 // timeout handle 8885 timeoutTimer, 8886 8887 // Url cleanup var 8888 urlAnchor, 8889 8890 // Request state (becomes false upon send and true upon completion) 8891 completed, 8892 8893 // To know if global events are to be dispatched 8894 fireGlobals, 8895 8896 // Loop variable 8897 i, 8898 8899 // uncached part of the url 8900 uncached, 8901 8902 // Create the final options object 8903 s = jQuery.ajaxSetup( {}, options ), 8904 8905 // Callbacks context 8906 callbackContext = s.context || s, 8907 8908 // Context for global events is callbackContext if it is a DOM node or jQuery collection 8909 globalEventContext = s.context && 8910 ( callbackContext.nodeType || callbackContext.jquery ) ? 8911 jQuery( callbackContext ) : 8912 jQuery.event, 8913 8914 // Deferreds 8915 deferred = jQuery.Deferred(), 8916 completeDeferred = jQuery.Callbacks( "once memory" ), 8917 8918 // Status-dependent callbacks 8919 statusCode = s.statusCode || {}, 8920 8921 // Headers (they are sent all at once) 8922 requestHeaders = {}, 8923 requestHeadersNames = {}, 8924 8925 // Default abort message 8926 strAbort = "canceled", 8927 8928 // Fake xhr 8929 jqXHR = { 8930 readyState: 0, 8931 8932 // Builds headers hashtable if needed 8933 getResponseHeader: function( key ) { 8934 var match; 8935 if ( completed ) { 8936 if ( !responseHeaders ) { 8937 responseHeaders = {}; 8938 while ( ( match = rheaders.exec( responseHeadersString ) ) ) { 8939 responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; 8940 } 8941 } 8942 match = responseHeaders[ key.toLowerCase() ]; 8943 } 8944 return match == null ? null : match; 8945 }, 8946 8947 // Raw string 8948 getAllResponseHeaders: function() { 8949 return completed ? responseHeadersString : null; 8950 }, 8951 8952 // Caches the header 8953 setRequestHeader: function( name, value ) { 8954 if ( completed == null ) { 8955 name = requestHeadersNames[ name.toLowerCase() ] = 8956 requestHeadersNames[ name.toLowerCase() ] || name; 8957 requestHeaders[ name ] = value; 8958 } 8959 return this; 8960 }, 8961 8962 // Overrides response content-type header 8963 overrideMimeType: function( type ) { 8964 if ( completed == null ) { 8965 s.mimeType = type; 8966 } 8967 return this; 8968 }, 8969 8970 // Status-dependent callbacks 8971 statusCode: function( map ) { 8972 var code; 8973 if ( map ) { 8974 if ( completed ) { 8975 8976 // Execute the appropriate callbacks 8977 jqXHR.always( map[ jqXHR.status ] ); 8978 } else { 8979 8980 // Lazy-add the new callbacks in a way that preserves old ones 8981 for ( code in map ) { 8982 statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; 8983 } 8984 } 8985 } 8986 return this; 8987 }, 8988 8989 // Cancel the request 8990 abort: function( statusText ) { 8991 var finalText = statusText || strAbort; 8992 if ( transport ) { 8993 transport.abort( finalText ); 8994 } 8995 done( 0, finalText ); 8996 return this; 8997 } 8998 }; 8999 9000 // Attach deferreds 9001 deferred.promise( jqXHR ); 9002 9003 // Add protocol if not provided (prefilters might expect it) 9004 // Handle falsy url in the settings object (#10093: consistency with old signature) 9005 // We also use the url parameter if available 9006 s.url = ( ( url || s.url || location.href ) + "" ) 9007 .replace( rprotocol, location.protocol + "//" ); 9008 9009 // Alias method option to type as per ticket #12004 9010 s.type = options.method || options.type || s.method || s.type; 9011 9012 // Extract dataTypes list 9013 s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; 9014 9015 // A cross-domain request is in order when the origin doesn't match the current origin. 9016 if ( s.crossDomain == null ) { 9017 urlAnchor = document.createElement( "a" ); 9018 9019 // Support: IE <=8 - 11, Edge 12 - 13 9020 // IE throws exception on accessing the href property if url is malformed, 9021 // e.g. http://example.com:80x/ 9022 try { 9023 urlAnchor.href = s.url; 9024 9025 // Support: IE <=8 - 11 only 9026 // Anchor's host property isn't correctly set when s.url is relative 9027 urlAnchor.href = urlAnchor.href; 9028 s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== 9029 urlAnchor.protocol + "//" + urlAnchor.host; 9030 } catch ( e ) { 9031 9032 // If there is an error parsing the URL, assume it is crossDomain, 9033 // it can be rejected by the transport if it is invalid 9034 s.crossDomain = true; 9035 } 9036 } 9037 9038 // Convert data if not already a string 9039 if ( s.data && s.processData && typeof s.data !== "string" ) { 9040 s.data = jQuery.param( s.data, s.traditional ); 9041 } 9042 9043 // Apply prefilters 9044 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); 9045 9046 // If request was aborted inside a prefilter, stop there 9047 if ( completed ) { 9048 return jqXHR; 9049 } 9050 9051 // We can fire global events as of now if asked to 9052 // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) 9053 fireGlobals = jQuery.event && s.global; 9054 9055 // Watch for a new set of requests 9056 if ( fireGlobals && jQuery.active++ === 0 ) { 9057 jQuery.event.trigger( "ajaxStart" ); 9058 } 9059 9060 // Uppercase the type 9061 s.type = s.type.toUpperCase(); 9062 9063 // Determine if request has content 9064 s.hasContent = !rnoContent.test( s.type ); 9065 9066 // Save the URL in case we're toying with the If-Modified-Since 9067 // and/or If-None-Match header later on 9068 // Remove hash to simplify url manipulation 9069 cacheURL = s.url.replace( rhash, "" ); 9070 9071 // More options handling for requests with no content 9072 if ( !s.hasContent ) { 9073 9074 // Remember the hash so we can put it back 9075 uncached = s.url.slice( cacheURL.length ); 9076 9077 // If data is available, append data to url 9078 if ( s.data ) { 9079 cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; 9080 9081 // #9682: remove data so that it's not used in an eventual retry 9082 delete s.data; 9083 } 9084 9085 // Add or update anti-cache param if needed 9086 if ( s.cache === false ) { 9087 cacheURL = cacheURL.replace( rantiCache, "$1" ); 9088 uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; 9089 } 9090 9091 // Put hash and anti-cache on the URL that will be requested (gh-1732) 9092 s.url = cacheURL + uncached; 9093 9094 // Change '%20' to '+' if this is encoded form body content (gh-2658) 9095 } else if ( s.data && s.processData && 9096 ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { 9097 s.data = s.data.replace( r20, "+" ); 9098 } 9099 9100 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. 9101 if ( s.ifModified ) { 9102 if ( jQuery.lastModified[ cacheURL ] ) { 9103 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); 9104 } 9105 if ( jQuery.etag[ cacheURL ] ) { 9106 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); 9107 } 9108 } 9109 9110 // Set the correct header, if data is being sent 9111 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { 9112 jqXHR.setRequestHeader( "Content-Type", s.contentType ); 9113 } 9114 9115 // Set the Accepts header for the server, depending on the dataType 9116 jqXHR.setRequestHeader( 9117 "Accept", 9118 s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? 9119 s.accepts[ s.dataTypes[ 0 ] ] + 9120 ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : 9121 s.accepts[ "*" ] 9122 ); 9123 9124 // Check for headers option 9125 for ( i in s.headers ) { 9126 jqXHR.setRequestHeader( i, s.headers[ i ] ); 9127 } 9128 9129 // Allow custom headers/mimetypes and early abort 9130 if ( s.beforeSend && 9131 ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { 9132 9133 // Abort if not done already and return 9134 return jqXHR.abort(); 9135 } 9136 9137 // Aborting is no longer a cancellation 9138 strAbort = "abort"; 9139 9140 // Install callbacks on deferreds 9141 completeDeferred.add( s.complete ); 9142 jqXHR.done( s.success ); 9143 jqXHR.fail( s.error ); 9144 9145 // Get transport 9146 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); 9147 9148 // If no transport, we auto-abort 9149 if ( !transport ) { 9150 done( -1, "No Transport" ); 9151 } else { 9152 jqXHR.readyState = 1; 9153 9154 // Send global event 9155 if ( fireGlobals ) { 9156 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); 9157 } 9158 9159 // If request was aborted inside ajaxSend, stop there 9160 if ( completed ) { 9161 return jqXHR; 9162 } 9163 9164 // Timeout 9165 if ( s.async && s.timeout > 0 ) { 9166 timeoutTimer = window.setTimeout( function() { 9167 jqXHR.abort( "timeout" ); 9168 }, s.timeout ); 9169 } 9170 9171 try { 9172 completed = false; 9173 transport.send( requestHeaders, done ); 9174 } catch ( e ) { 9175 9176 // Rethrow post-completion exceptions 9177 if ( completed ) { 9178 throw e; 9179 } 9180 9181 // Propagate others as results 9182 done( -1, e ); 9183 } 9184 } 9185 9186 // Callback for when everything is done 9187 function done( status, nativeStatusText, responses, headers ) { 9188 var isSuccess, success, error, response, modified, 9189 statusText = nativeStatusText; 9190 9191 // Ignore repeat invocations 9192 if ( completed ) { 9193 return; 9194 } 9195 9196 completed = true; 9197 9198 // Clear timeout if it exists 9199 if ( timeoutTimer ) { 9200 window.clearTimeout( timeoutTimer ); 9201 } 9202 9203 // Dereference transport for early garbage collection 9204 // (no matter how long the jqXHR object will be used) 9205 transport = undefined; 9206 9207 // Cache response headers 9208 responseHeadersString = headers || ""; 9209 9210 // Set readyState 9211 jqXHR.readyState = status > 0 ? 4 : 0; 9212 9213 // Determine if successful 9214 isSuccess = status >= 200 && status < 300 || status === 304; 9215 9216 // Get response data 9217 if ( responses ) { 9218 response = ajaxHandleResponses( s, jqXHR, responses ); 9219 } 9220 9221 // Convert no matter what (that way responseXXX fields are always set) 9222 response = ajaxConvert( s, response, jqXHR, isSuccess ); 9223 9224 // If successful, handle type chaining 9225 if ( isSuccess ) { 9226 9227 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. 9228 if ( s.ifModified ) { 9229 modified = jqXHR.getResponseHeader( "Last-Modified" ); 9230 if ( modified ) { 9231 jQuery.lastModified[ cacheURL ] = modified; 9232 } 9233 modified = jqXHR.getResponseHeader( "etag" ); 9234 if ( modified ) { 9235 jQuery.etag[ cacheURL ] = modified; 9236 } 9237 } 9238 9239 // if no content 9240 if ( status === 204 || s.type === "HEAD" ) { 9241 statusText = "nocontent"; 9242 9243 // if not modified 9244 } else if ( status === 304 ) { 9245 statusText = "notmodified"; 9246 9247 // If we have data, let's convert it 9248 } else { 9249 statusText = response.state; 9250 success = response.data; 9251 error = response.error; 9252 isSuccess = !error; 9253 } 9254 } else { 9255 9256 // Extract error from statusText and normalize for non-aborts 9257 error = statusText; 9258 if ( status || !statusText ) { 9259 statusText = "error"; 9260 if ( status < 0 ) { 9261 status = 0; 9262 } 9263 } 9264 } 9265 9266 // Set data for the fake xhr object 9267 jqXHR.status = status; 9268 jqXHR.statusText = ( nativeStatusText || statusText ) + ""; 9269 9270 // Success/Error 9271 if ( isSuccess ) { 9272 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); 9273 } else { 9274 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); 9275 } 9276 9277 // Status-dependent callbacks 9278 jqXHR.statusCode( statusCode ); 9279 statusCode = undefined; 9280 9281 if ( fireGlobals ) { 9282 globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", 9283 [ jqXHR, s, isSuccess ? success : error ] ); 9284 } 9285 9286 // Complete 9287 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); 9288 9289 if ( fireGlobals ) { 9290 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); 9291 9292 // Handle the global AJAX counter 9293 if ( !( --jQuery.active ) ) { 9294 jQuery.event.trigger( "ajaxStop" ); 9295 } 9296 } 9297 } 9298 9299 return jqXHR; 9300 }, 9301 9302 getJSON: function( url, data, callback ) { 9303 return jQuery.get( url, data, callback, "json" ); 9304 }, 9305 9306 getScript: function( url, callback ) { 9307 return jQuery.get( url, undefined, callback, "script" ); 9308 } 9309} ); 9310 9311jQuery.each( [ "get", "post" ], function( i, method ) { 9312 jQuery[ method ] = function( url, data, callback, type ) { 9313 9314 // Shift arguments if data argument was omitted 9315 if ( jQuery.isFunction( data ) ) { 9316 type = type || callback; 9317 callback = data; 9318 data = undefined; 9319 } 9320 9321 // The url can be an options object (which then must have .url) 9322 return jQuery.ajax( jQuery.extend( { 9323 url: url, 9324 type: method, 9325 dataType: type, 9326 data: data, 9327 success: callback 9328 }, jQuery.isPlainObject( url ) && url ) ); 9329 }; 9330} ); 9331 9332 9333jQuery._evalUrl = function( url ) { 9334 return jQuery.ajax( { 9335 url: url, 9336 9337 // Make this explicit, since user can override this through ajaxSetup (#11264) 9338 type: "GET", 9339 dataType: "script", 9340 cache: true, 9341 async: false, 9342 global: false, 9343 "throws": true 9344 } ); 9345}; 9346 9347 9348jQuery.fn.extend( { 9349 wrapAll: function( html ) { 9350 var wrap; 9351 9352 if ( this[ 0 ] ) { 9353 if ( jQuery.isFunction( html ) ) { 9354 html = html.call( this[ 0 ] ); 9355 } 9356 9357 // The elements to wrap the target around 9358 wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); 9359 9360 if ( this[ 0 ].parentNode ) { 9361 wrap.insertBefore( this[ 0 ] ); 9362 } 9363 9364 wrap.map( function() { 9365 var elem = this; 9366 9367 while ( elem.firstElementChild ) { 9368 elem = elem.firstElementChild; 9369 } 9370 9371 return elem; 9372 } ).append( this ); 9373 } 9374 9375 return this; 9376 }, 9377 9378 wrapInner: function( html ) { 9379 if ( jQuery.isFunction( html ) ) { 9380 return this.each( function( i ) { 9381 jQuery( this ).wrapInner( html.call( this, i ) ); 9382 } ); 9383 } 9384 9385 return this.each( function() { 9386 var self = jQuery( this ), 9387 contents = self.contents(); 9388 9389 if ( contents.length ) { 9390 contents.wrapAll( html ); 9391 9392 } else { 9393 self.append( html ); 9394 } 9395 } ); 9396 }, 9397 9398 wrap: function( html ) { 9399 var isFunction = jQuery.isFunction( html ); 9400 9401 return this.each( function( i ) { 9402 jQuery( this ).wrapAll( i
vendor: 2,399 bytes, lines 9402-9494
9402sFunction ? html.call( this, i ) : html ); 9403 } ); 9404 }, 9405 9406 unwrap: function( selector ) { 9407 this.parent( selector ).not( "body" ).each( function() { 9408 jQuery( this ).replaceWith( this.childNodes ); 9409 } ); 9410 return this; 9411 } 9412} ); 9413 9414 9415jQuery.expr.pseudos.hidden = function( elem ) { 9416 return !jQuery.expr.pseudos.visible( elem ); 9417}; 9418jQuery.expr.pseudos.visible = function( elem ) { 9419 return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); 9420}; 9421 9422 9423 9424 9425jQuery.ajaxSettings.xhr = function() { 9426 try { 9427 return new window.XMLHttpRequest(); 9428 } catch ( e ) {} 9429}; 9430 9431var xhrSuccessStatus = { 9432 9433 // File protocol always yields status code 0, assume 200 9434 0: 200, 9435 9436 // Support: IE <=9 only 9437 // #1450: sometimes IE returns 1223 when it should be 204 9438 1223: 204 9439 }, 9440 xhrSupported = jQuery.ajaxSettings.xhr(); 9441 9442support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); 9443support.ajax = xhrSupported = !!xhrSupported; 9444 9445jQuery.ajaxTransport( function( options ) { 9446 var callback, errorCallback; 9447 9448 // Cross domain only allowed if supported through XMLHttpRequest 9449 if ( support.cors || xhrSupported && !options.crossDomain ) { 9450 return { 9451 send: function( headers, complete ) { 9452 var i, 9453 xhr = options.xhr(); 9454 9455 xhr.open( 9456 options.type, 9457 options.url, 9458 options.async, 9459 options.username, 9460 options.password 9461 ); 9462 9463 // Apply custom fields if provided 9464 if ( options.xhrFields ) { 9465 for ( i in options.xhrFields ) { 9466 xhr[ i ] = options.xhrFields[ i ]; 9467 } 9468 } 9469 9470 // Override mime type if needed 9471 if ( options.mimeType && xhr.overrideMimeType ) { 9472 xhr.overrideMimeType( options.mimeType ); 9473 } 9474 9475 // X-Requested-With header 9476 // For cross-domain requests, seeing as conditions for a preflight are 9477 // akin to a jigsaw puzzle, we simply never set it to be sure. 9478 // (it can always be set on a per-request basis or even using ajaxSetup) 9479 // For same-domain requests, won't change header if already provided. 9480 if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { 9481 headers[ "X-Requested-With" ] = "XMLHttpRequest"; 9482 } 9483 9484 // Set headers 9485 for ( i in headers ) { 9486 xhr.setRequestHeader( i, headers[ i ] ); 9487 } 9488 9489 // Callback 9490 callback = function( type ) { 9491 return function() { 9492 if ( callback ) { 9493 callback = errorCallback = xhr.onload = 9494 xhr.onerror = xhr.onabort = xhr.on
vendor: 5,102 bytes, lines 9494-9699
9494readystatechange = null; 9495 9496 if ( type === "abort" ) { 9497 xhr.abort(); 9498 } else if ( type === "error" ) { 9499 9500 // Support: IE <=9 only 9501 // On a manual native abort, IE9 throws 9502 // errors on any property access that is not readyState 9503 if ( typeof xhr.status !== "number" ) { 9504 complete( 0, "error" ); 9505 } else { 9506 complete( 9507 9508 // File: protocol always yields status 0; see #8605, #14207 9509 xhr.status, 9510 xhr.statusText 9511 ); 9512 } 9513 } else { 9514 complete( 9515 xhrSuccessStatus[ xhr.status ] || xhr.status, 9516 xhr.statusText, 9517 9518 // Support: IE <=9 only 9519 // IE9 has no XHR2 but throws on binary (trac-11426) 9520 // For XHR2 non-text, let the caller handle it (gh-2498) 9521 ( xhr.responseType || "text" ) !== "text" || 9522 typeof xhr.responseText !== "string" ? 9523 { binary: xhr.response } : 9524 { text: xhr.responseText }, 9525 xhr.getAllResponseHeaders() 9526 ); 9527 } 9528 } 9529 }; 9530 }; 9531 9532 // Listen to events 9533 xhr.onload = callback(); 9534 errorCallback = xhr.onerror = callback( "error" ); 9535 9536 // Support: IE 9 only 9537 // Use onreadystatechange to replace onabort 9538 // to handle uncaught aborts 9539 if ( xhr.onabort !== undefined ) { 9540 xhr.onabort = errorCallback; 9541 } else { 9542 xhr.onreadystatechange = function() { 9543 9544 // Check readyState before timeout as it changes 9545 if ( xhr.readyState === 4 ) { 9546 9547 // Allow onerror to be called first, 9548 // but that will not handle a native abort 9549 // Also, save errorCallback to a variable 9550 // as xhr.onerror cannot be accessed 9551 window.setTimeout( function() { 9552 if ( callback ) { 9553 errorCallback(); 9554 } 9555 } ); 9556 } 9557 }; 9558 } 9559 9560 // Create the abort callback 9561 callback = callback( "abort" ); 9562 9563 try { 9564 9565 // Do send the request (this may raise an exception) 9566 xhr.send( options.hasContent && options.data || null ); 9567 } catch ( e ) { 9568 9569 // #14683: Only rethrow if this hasn't been notified as an error yet 9570 if ( callback ) { 9571 throw e; 9572 } 9573 } 9574 }, 9575 9576 abort: function() { 9577 if ( callback ) { 9578 callback(); 9579 } 9580 } 9581 }; 9582 } 9583} ); 9584 9585 9586 9587 9588// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) 9589jQuery.ajaxPrefilter( function( s ) { 9590 if ( s.crossDomain ) { 9591 s.contents.script = false; 9592 } 9593} ); 9594 9595// Install script dataType 9596jQuery.ajaxSetup( { 9597 accepts: { 9598 script: "text/javascript, application/javascript, " + 9599 "application/ecmascript, application/x-ecmascript" 9600 }, 9601 contents: { 9602 script: /\b(?:java|ecma)script\b/ 9603 }, 9604 converters: { 9605 "text script": function( text ) { 9606 jQuery.globalEval( text ); 9607 return text; 9608 } 9609 } 9610} ); 9611 9612// Handle cache's special case and crossDomain 9613jQuery.ajaxPrefilter( "script", function( s ) { 9614 if ( s.cache === undefined ) { 9615 s.cache = false; 9616 } 9617 if ( s.crossDomain ) { 9618 s.type = "GET"; 9619 } 9620} ); 9621 9622// Bind script tag hack transport 9623jQuery.ajaxTransport( "script", function( s ) { 9624 9625 // This transport only deals with cross domain requests 9626 if ( s.crossDomain ) { 9627 var script, callback; 9628 return { 9629 send: function( _, complete ) { 9630 script = jQuery( "<script>" ).prop( { 9631 charset: s.scriptCharset, 9632 src: s.url 9633 } ).on( 9634 "load error", 9635 callback = function( evt ) { 9636 script.remove(); 9637 callback = null; 9638 if ( evt ) { 9639 complete( evt.type === "error" ? 404 : 200, evt.type ); 9640 } 9641 } 9642 ); 9643 9644 // Use native DOM manipulation to avoid our domManip AJAX trickery 9645 document.head.appendChild( script[ 0 ] ); 9646 }, 9647 abort: function() { 9648 if ( callback ) { 9649 callback(); 9650 } 9651 } 9652 }; 9653 } 9654} ); 9655 9656 9657 9658 9659var oldCallbacks = [], 9660 rjsonp = /(=)\?(?=&|$)|\?\?/; 9661 9662// Default jsonp settings 9663jQuery.ajaxSetup( { 9664 jsonp: "callback", 9665 jsonpCallback: function() { 9666 var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); 9667 this[ callback ] = true; 9668 return callback; 9669 } 9670} ); 9671 9672// Detect, normalize options and install callbacks for jsonp requests 9673jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { 9674 9675 var callbackName, overwritten, responseContainer, 9676 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? 9677 "url" : 9678 typeof s.data === "string" && 9679 ( s.contentType || "" ) 9680 .indexOf( "application/x-www-form-urlencoded" ) === 0 && 9681 rjsonp.test( s.data ) && "data" 9682 ); 9683 9684 // Handle iff the expected data type is "jsonp" or we have a parameter to set 9685 if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { 9686 9687 // Get callback name, remembering preexisting value associated with it 9688 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? 9689 s.jsonpCallback() : 9690 s.jsonpCallback; 9691 9692 // Insert callback into url or form data 9693 if ( jsonProp ) { 9694 s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); 9695 } else if ( s.jsonp !== false ) { 9696 s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; 9697 } 9698 9699 // Use data converter to retrieve json after script execution
vendor: 2,994 bytes, lines 9700-9809
9700 s.converters[ "script json" ] = function() { 9701 if ( !responseContainer ) { 9702 jQuery.error( callbackName + " was not called" ); 9703 } 9704 return responseContainer[ 0 ]; 9705 }; 9706 9707 // Force json dataType 9708 s.dataTypes[ 0 ] = "json"; 9709 9710 // Install callback 9711 overwritten = window[ callbackName ]; 9712 window[ callbackName ] = function() { 9713 responseContainer = arguments; 9714 }; 9715 9716 // Clean-up function (fires after converters) 9717 jqXHR.always( function() { 9718 9719 // If previous value didn't exist - remove it 9720 if ( overwritten === undefined ) { 9721 jQuery( window ).removeProp( callbackName ); 9722 9723 // Otherwise restore preexisting value 9724 } else { 9725 window[ callbackName ] = overwritten; 9726 } 9727 9728 // Save back as free 9729 if ( s[ callbackName ] ) { 9730 9731 // Make sure that re-using the options doesn't screw things around 9732 s.jsonpCallback = originalSettings.jsonpCallback; 9733 9734 // Save the callback name for future use 9735 oldCallbacks.push( callbackName ); 9736 } 9737 9738 // Call if it was a function and we have a response 9739 if ( responseContainer && jQuery.isFunction( overwritten ) ) { 9740 overwritten( responseContainer[ 0 ] ); 9741 } 9742 9743 responseContainer = overwritten = undefined; 9744 } ); 9745 9746 // Delegate to script 9747 return "script"; 9748 } 9749} ); 9750 9751 9752 9753 9754// Support: Safari 8 only 9755// In Safari 8 documents created via document.implementation.createHTMLDocument 9756// collapse sibling forms: the second one becomes a child of the first one. 9757// Because of that, this security measure has to be disabled in Safari 8. 9758// https://bugs.webkit.org/show_bug.cgi?id=137337 9759support.createHTMLDocument = ( function() { 9760 var body = document.implementation.createHTMLDocument( "" ).body; 9761 body.innerHTML = "<form></form><form></form>"; 9762 return body.childNodes.length === 2; 9763} )(); 9764 9765 9766// Argument "data" should be string of html 9767// context (optional): If specified, the fragment will be created in this context, 9768// defaults to document 9769// keepScripts (optional): If true, will include scripts passed in the html string 9770jQuery.parseHTML = function( data, context, keepScripts ) { 9771 if ( typeof data !== "string" ) { 9772 return []; 9773 } 9774 if ( typeof context === "boolean" ) { 9775 keepScripts = context; 9776 context = false; 9777 } 9778 9779 var base, parsed, scripts; 9780 9781 if ( !context ) { 9782 9783 // Stop scripts or inline event handlers from being executed immediately 9784 // by using document.implementation 9785 if ( support.createHTMLDocument ) { 9786 context = document.implementation.createHTMLDocument( "" ); 9787 9788 // Set the base href for the created document 9789 // so any parsed elements with URLs 9790 // are based on the document's URL (gh-2965) 9791 base = context.createElement( "base" ); 9792 base.href = document.location.href; 9793 context.head.appendChild( base ); 9794 } else { 9795 context = document; 9796 } 9797 } 9798 9799 parsed = rsingleTag.exec( data ); 9800 scripts = !keepScripts && []; 9801 9802 // Single tag 9803 if ( parsed ) { 9804 return [ context.createElement( parsed[ 1 ] ) ]; 9805 } 9806 9807 parsed = buildFragment( [ data ], context, scripts ); 9808 9809 if ( scripts && scripts.length ) {
vendor: 4,095 bytes, lines 9810-9980
9810 jQuery( scripts ).remove(); 9811 } 9812 9813 return jQuery.merge( [], parsed.childNodes ); 9814}; 9815 9816 9817/** 9818 * Load a url into a page 9819 */ 9820jQuery.fn.load = function( url, params, callback ) { 9821 var selector, type, response, 9822 self = this, 9823 off = url.indexOf( " " ); 9824 9825 if ( off > -1 ) { 9826 selector = stripAndCollapse( url.slice( off ) ); 9827 url = url.slice( 0, off ); 9828 } 9829 9830 // If it's a function 9831 if ( jQuery.isFunction( params ) ) { 9832 9833 // We assume that it's the callback 9834 callback = params; 9835 params = undefined; 9836 9837 // Otherwise, build a param string 9838 } else if ( params && typeof params === "object" ) { 9839 type = "POST"; 9840 } 9841 9842 // If we have elements to modify, make the request 9843 if ( self.length > 0 ) { 9844 jQuery.ajax( { 9845 url: url, 9846 9847 // If "type" variable is undefined, then "GET" method will be used. 9848 // Make value of this field explicit since 9849 // user can override it through ajaxSetup method 9850 type: type || "GET", 9851 dataType: "html", 9852 data: params 9853 } ).done( function( responseText ) { 9854 9855 // Save response for use in complete callback 9856 response = arguments; 9857 9858 self.html( selector ? 9859 9860 // If a selector was specified, locate the right elements in a dummy div 9861 // Exclude scripts to avoid IE 'Permission Denied' errors 9862 jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) : 9863 9864 // Otherwise use the full result 9865 responseText ); 9866 9867 // If the request succeeds, this function gets "data", "status", "jqXHR" 9868 // but they are ignored because response was set above. 9869 // If it fails, this function gets "jqXHR", "status", "error" 9870 } ).always( callback && function( jqXHR, status ) { 9871 self.each( function() { 9872 callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); 9873 } ); 9874 } ); 9875 } 9876 9877 return this; 9878}; 9879 9880 9881 9882 9883// Attach a bunch of functions for handling common AJAX events 9884jQuery.each( [ 9885 "ajaxStart", 9886 "ajaxStop", 9887 "ajaxComplete", 9888 "ajaxError", 9889 "ajaxSuccess", 9890 "ajaxSend" 9891], function( i, type ) { 9892 jQuery.fn[ type ] = function( fn ) { 9893 return this.on( type, fn ); 9894 }; 9895} ); 9896 9897 9898 9899 9900jQuery.expr.pseudos.animated = function( elem ) { 9901 return jQuery.grep( jQuery.timers, function( fn ) { 9902 return elem === fn.elem; 9903 } ).length; 9904}; 9905 9906 9907 9908 9909jQuery.offset = { 9910 setOffset: function( elem, options, i ) { 9911 var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, 9912 position = jQuery.css( elem, "position" ), 9913 curElem = jQuery( elem ), 9914 props = {}; 9915 9916 // Set position first, in-case top/left are set even on static elem 9917 if ( position === "static" ) { 9918 elem.style.position = "relative"; 9919 } 9920 9921 curOffset = curElem.offset(); 9922 curCSSTop = jQuery.css( elem, "top" ); 9923 curCSSLeft = jQuery.css( elem, "left" ); 9924 calculatePosition = ( position === "absolute" || position === "fixed" ) && 9925 ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; 9926 9927 // Need to be able to calculate position if either 9928 // top or left is auto and position is either absolute or fixed 9929 if ( calculatePosition ) { 9930 curPosition = curElem.position(); 9931 curTop = curPosition.top; 9932 curLeft = curPosition.left; 9933 9934 } else { 9935 curTop = parseFloat( curCSSTop ) || 0; 9936 curLeft = parseFloat( curCSSLeft ) || 0; 9937 } 9938 9939 if ( jQuery.isFunction( options ) ) { 9940 9941 // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) 9942 options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); 9943 } 9944 9945 if ( options.top != null ) { 9946 props.top = ( options.top - curOffset.top ) + curTop; 9947 } 9948 if ( options.left != null ) { 9949 props.left = ( options.left - curOffset.left ) + curLeft; 9950 } 9951 9952 if ( "using" in options ) { 9953 options.using.call( elem, props ); 9954 9955 } else { 9956 curElem.css( props ); 9957 } 9958 } 9959}; 9960 9961jQuery.fn.extend( { 9962 offset: function( options ) { 9963 9964 // Preserve chaining for setter 9965 if ( arguments.length ) { 9966 return options === undefined ? 9967 this : 9968 this.each( function( i ) { 9969 jQuery.offset.setOffset( this, options, i ); 9970 } ); 9971 } 9972 9973 var doc, docElem, rect, win, 9974 elem = this[ 0 ]; 9975 9976 if ( !elem ) { 9977 return; 9978 } 9979 9980 // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
vendor: 4,119 bytes, lines 9981-10119
9981 // Support: IE <=11 only 9982 // Running getBoundingClientRect on a 9983 // disconnected node in IE throws an error 9984 if ( !elem.getClientRects().length ) { 9985 return { top: 0, left: 0 }; 9986 } 9987 9988 rect = elem.getBoundingClientRect(); 9989 9990 doc = elem.ownerDocument; 9991 docElem = doc.documentElement; 9992 win = doc.defaultView; 9993 9994 return { 9995 top: rect.top + win.pageYOffset - docElem.clientTop, 9996 left: rect.left + win.pageXOffset - docElem.clientLeft 9997 }; 9998 }, 9999 10000 position: function() { 10001 if ( !this[ 0 ] ) { 10002 return; 10003 } 10004 10005 var offsetParent, offset, 10006 elem = this[ 0 ], 10007 parentOffset = { top: 0, left: 0 }; 10008 10009 // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, 10010 // because it is its only offset parent 10011 if ( jQuery.css( elem, "position" ) === "fixed" ) { 10012 10013 // Assume getBoundingClientRect is there when computed position is fixed 10014 offset = elem.getBoundingClientRect(); 10015 10016 } else { 10017 10018 // Get *real* offsetParent 10019 offsetParent = this.offsetParent(); 10020 10021 // Get correct offsets 10022 offset = this.offset(); 10023 if ( !nodeName( offsetParent[ 0 ], "html" ) ) { 10024 parentOffset = offsetParent.offset(); 10025 } 10026 10027 // Add offsetParent borders 10028 parentOffset = { 10029 top: parentOffset.top + jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ), 10030 left: parentOffset.left + jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ) 10031 }; 10032 } 10033 10034 // Subtract parent offsets and element margins 10035 return { 10036 top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), 10037 left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) 10038 }; 10039 }, 10040 10041 // This method will return documentElement in the following cases: 10042 // 1) For the element inside the iframe without offsetParent, this method will return 10043 // documentElement of the parent window 10044 // 2) For the hidden or detached element 10045 // 3) For body or html element, i.e. in case of the html node - it will return itself 10046 // 10047 // but those exceptions were never presented as a real life use-cases 10048 // and might be considered as more preferable results. 10049 // 10050 // This logic, however, is not guaranteed and can change at any point in the future 10051 offsetParent: function() { 10052 return this.map( function() { 10053 var offsetParent = this.offsetParent; 10054 10055 while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) { 10056 offsetParent = offsetParent.offsetParent; 10057 } 10058 10059 return offsetParent || documentElement; 10060 } ); 10061 } 10062} ); 10063 10064// Create scrollLeft and scrollTop methods 10065jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { 10066 var top = "pageYOffset" === prop; 10067 10068 jQuery.fn[ method ] = function( val ) { 10069 return access( this, function( elem, method, val ) { 10070 10071 // Coalesce documents and windows 10072 var win; 10073 if ( jQuery.isWindow( elem ) ) { 10074 win = elem; 10075 } else if ( elem.nodeType === 9 ) { 10076 win = elem.defaultView; 10077 } 10078 10079 if ( val === undefined ) { 10080 return win ? win[ prop ] : elem[ method ]; 10081 } 10082 10083 if ( win ) { 10084 win.scrollTo( 10085 !top ? val : win.pageXOffset, 10086 top ? val : win.pageYOffset 10087 ); 10088 10089 } else { 10090 elem[ method ] = val; 10091 } 10092 }, method, val, arguments.length ); 10093 }; 10094} ); 10095 10096// Support: Safari <=7 - 9.1, Chrome <=37 - 49 10097// Add the top/left cssHooks using jQuery.fn.position 10098// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 10099// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 10100// getComputedStyle returns percent when specified for top/left/bottom/right; 10101// rather than make the css module depend on the offset module, just check for it here 10102jQuery.each( [ "top", "left" ], function( i, prop ) { 10103 jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, 10104 function( elem, computed ) { 10105 if ( computed ) { 10106 computed = curCSS( elem, prop ); 10107 10108 // If curCSS returns percentage, fallback to offset 10109 return rnumnonpx.test( computed ) ? 10110 jQuery( elem ).position()[ prop ] + "px" : 10111 computed; 10112 } 10113 } 10114 ); 10115} ); 10116 10117 10118// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods 10119jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
10120 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, 10121 function( defaultExtra, funcName ) { 10122 10123 // Margin is only for outerHeight, outerWidth 10124 jQuery.fn[ funcName ] = function( margin, value ) { 10125 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), 10126 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); 10127 10128 return access( this, function( elem, type, value ) { 10129 var doc; 10130 10131 if ( jQuery.isWindow( elem ) ) { 10132 10133 // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) 10134 return funcName.indexOf( "outer" ) === 0 ? 10135 elem[ "inner" + name ] : 10136 elem.document.documentElement[ "client" + name ]; 10137 } 10138 10139 // Get document width or height 10140 if ( elem.nodeType === 9 ) { 10141 doc = elem.documentElement; 10142 10143 // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], 10144 // whichever is greatest 10145 return Math.max( 10146 elem.body[ "scroll" + name ], doc[ "scroll" + name ], 10147 elem.body[ "offset" + name ], doc[ "offset" + name ], 10148 doc[ "client" + name ] 10149 ); 10150 } 10151 10152 return value === undefined ? 10153 10154 // Get width or height on the element, requesting but not forcing parseFloat 10155 jQuery.css( elem, type, extra ) : 10156 10157 // Set width or height on the element 10158 jQuery.style( elem, type, value, extra ); 10159 }, type, chainable ? margin : undefined, chainable ); 10160 }; 10161 } ); 10162} ); 10163 10164 10165jQuery.fn.extend( { 10166 10167 bind: function( types, data, fn ) { 10168 return this.on( types, null, data, fn ); 10169 }, 10170 unbind: function( types, fn ) { 10171 return this.off( types, null, fn ); 10172 }, 10173 10174 delegate: function( selector, types, data, fn ) { 10175 return this.on( types, selector, data, fn ); 10176 }, 10177 undelegate: function( selector, types, fn ) { 10178 10179 // ( namespace ) or ( selector, types [, fn] ) 10180 return arguments.length === 1 ? 10181 this.off( selector, "**" ) : 10182 this.off( types, selector || "**", fn ); 10183 } 10184} ); 10185 10186jQuery.holdReady = function( hold ) { 10187 if ( hold ) { 10188 jQuery.readyWait++; 10189 } else { 10190 jQuery.ready( true ); 10191 } 10192}; 10193jQuery.isArray = Array.isArray; 10194jQuery.parseJSON = JSON.parse; 10195jQuery.nodeName = nodeName; 10196 10197 10198 10199 10200// Register as a named AMD module, since jQuery can be concatenated with other 10201// files that may use define, but not via a proper concatenation script that 10202// understands anonymous AMD modules. A named AMD is safest and most robust 10203// way to register. Lowercase jquery is used because AMD module names are 10204// derived from file names, and jQuery is normally delivered in a lowercase 10205// file name. Do this after creating the global so that if an AMD module wants 10206// to call noConflict to hide this version of jQuery, it will work. 10207 10208// Note that for maximum portability, libraries that are not jQuery should 10209// declare themselves as anonymous modules, and avoid setting a global if an 10210// AMD loader is present. jQuery is a special case. For more information, see 10211// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon 10212 10213if ( typeof define === "function" && define.amd ) { 10214 define( "jquery", [], function() { 10215 return jQuery; 10216 } ); 10217} 10218 10219 10220 10221 10222var 10223 10224 // Map over jQuery in case of overwrite 10225 _jQuery = window.jQuery, 10226 10227 // Map over the $ in case of overwrite 10228 _$ = window.$; 10229 10230jQuery.noConflict = function( deep ) { 10231 if ( window.$ === jQuery ) { 10232 window.$ = _$; 10233 } 10234 10235 if ( deep && window.jQuery === jQuery ) { 10236 window.jQuery = _jQuery; 10237 } 10238 10239 return jQuery; 10240}; 10241 10242// Expose jQuery and $ identifiers, even in AMD 10243// (#7102#comment:10, https://github.com/jquery/jquery/pull/557) 10244// and CommonJS for browser emulators (#13566) 10245if ( !noGlobal ) { 10246 window.jQuery = window.$ = jQuery; 10247} 10248 10249 10250 10251 10252return jQuery; 10253} ); 10254 10255/** 10256 * @license AngularJS v1.6.3 10257 * (c) 2010-2017 Google, Inc. http://angularjs.org 10258 * License: MIT 10259 */ 10260(function(window) {'use strict'; 10261 10262/** 10263 * @description 10264 * 10265 * This object provides a utility for producing rich Error messages within 10266 * Angular. It can be called as follows: 10267 * 10268 * var exampleMinErr = minErr('example'); 10269 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); 10270 * 10271 * The above creates an instance of minErr in the example namespace. The 10272 * resulting error will have a namespaced error code of example.one. The 10273 * resulting error will replace {0} with the value of foo, and {1} with the 10274 * value of bar. The object is not restricted in the number of arguments it can 10275 * take. 10276 * 10277 * If fewer arguments are specified than necessary for interpolation, the extra 10278 * interpolation markers will be preserved in the final string. 10279 * 10280 * Since data will be parsed statically during a build step, some restrictions 10281 * are applied with respect to how minErr instances are created and called. 10282 * Instances should have names of the form namespaceMinErr for a minErr created 10283 * using minErr('namespace') . Error codes, namespaces and template strings 10284 * should all be static strings, not variables or general expressions. 10285 * 10286 * @param {string} module The namespace to use for the new minErr instance. 10287 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning 10288 * error from returned function, for cases when a particular type of error is useful. 10289 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance 10290 */ 10291 10292function minErr(module, ErrorConstructor) { 10293 ErrorConstructor = ErrorConstructor || Error; 10294 return function() { 10295 var code = arguments[0], 10296 template = arguments[1], 10297 message = '[' + (module ? module + ':' : '') + code + '] ', 10298 templateArgs = sliceArgs(arguments, 2).map(function(arg) { 10299 return toDebugString(arg, minErrConfig.objectMaxDepth); 10300 }), 10301 paramPrefix, i; 10302 10303 message += template.replace(/\{\d+\}/g, function(match) { 10304 var index = +match.slice(1, -1); 10305 10306 if (index < templateArgs.length) { 10307 return templateArgs[index]; 10308 } 10309 10310 return match; 10311 }); 10312 10313 message += '\nhttp://errors.angularjs.org/1.6.3/' + 10314 (module ? module + '/' : '') + code; 10315 10316 for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
10317 message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]); 10318 } 10319 10320 return new ErrorConstructor(message); 10321 }; 10322} 10323 10324/* We need to tell ESLint what variables are being exported */ 10325/* exported 10326 angular, 10327 msie, 10328 jqLite, 10329 jQuery, 10330 slice, 10331 splice, 10332 push, 10333 toString, 10334 minErrConfig, 10335 errorHandlingConfig, 10336 isValidObjectMaxDepth, 10337 ngMinErr, 10338 angularModule, 10339 uid, 10340 REGEX_STRING_REGEXP, 10341 VALIDITY_STATE_PROPERTY, 10342 10343 lowercase, 10344 uppercase, 10345 manualLowercase, 10346 manualUppercase, 10347 nodeName_, 10348 isArrayLike, 10349 forEach, 10350 forEachSorted, 10351 reverseParams, 10352 nextUid, 10353 setHashKey, 10354 extend, 10355 toInt, 10356 inherit, 10357 merge, 10358 noop, 10359 identity, 10360 valueFn, 10361 isUndefined, 10362 isDefined, 10363 isObject, 10364 isBlankObject, 10365 isString, 10366 isNumber, 10367 isNumberNaN, 10368 isDate, 10369 isArray, 10370 isFunction, 10371 isRegExp, 10372 isWindow, 10373 isScope, 10374 isFile, 10375 isFormData, 10376 isBlob, 10377 isBoolean, 10378 isPromiseLike, 10379 trim, 10380 escapeForRegexp, 10381 isElement, 10382 makeMap, 10383 includes, 10384 arrayRemove, 10385 copy, 10386 equals, 10387 csp, 10388 jq, 10389 concat, 10390 sliceArgs, 10391 bind, 10392 toJsonReplacer, 10393 toJson, 10394 fromJson, 10395 convertTimezoneToLocal, 10396 timezoneToOffset, 10397 startingTag, 10398 tryDecodeURIComponent, 10399 parseKeyValue, 10400 toKeyValue, 10401 encodeUriSegment, 10402 encodeUriQuery, 10403 angularInit, 10404 bootstrap, 10405 getTestability, 10406 snake_case, 10407 bindJQuery, 10408 assertArg, 10409 assertArgFn, 10410 assertNotHasOwnProperty, 10411 getter, 10412 getBlockNodes, 10413 hasOwnProperty, 10414 createMap, 10415 stringify, 10416 10417 NODE_TYPE_ELEMENT, 10418 NODE_TYPE_ATTRIBUTE, 10419 NODE_TYPE_TEXT, 10420 NODE_TYPE_COMMENT, 10421 NODE_TYPE_DOCUMENT, 10422 NODE_TYPE_DOCUMENT_FRAGMENT 10423*/ 10424 10425//////////////////////////////////// 10426 10427/** 10428 * @ngdoc module 10429 * @name ng 10430 * @module ng 10431 * @installation 10432 * @description 10433 * 10434 * # ng (core module) 10435 * The ng module is loaded by default when an AngularJS application is started. The module itself 10436 * contains the essential components for an AngularJS application to function. The table below 10437 * lists a high level breakdown of each of the services/factories, filters, directives and testing 10438 * components available within this core module. 10439 * 10440 * <div doc-module-components="ng"></div> 10441 */ 10442 10443var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; 10444 10445// The name of a form control's ValidityState property. 10446// This is used so that it's possible for internal tests to create mock ValidityStates. 10447var VALIDITY_STATE_PROPERTY = 'validity'; 10448 10449 10450var hasOwnProperty = Object.prototype.hasOwnProperty; 10451 10452var minErrConfig = { 10453 objectMaxDepth: 5 10454}; 10455 10456 /** 10457 * @ngdoc function 10458 * @name angular.errorHandlingConfig 10459 * @module ng 10460 * @kind function 10461 * 10462 * @description 10463 * Configure several aspects of error handling in AngularJS if used as a setter or return the 10464 * current configuration if used as a getter. The following options are supported: 10465 * 10466 * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages. 10467 * 10468 * Omitted or undefined options will leave the corresponding configuration values unchanged. 10469 * 10470 * @param {Object=} config - The configuration object. May only contain the options that need to be 10471 * updated. Supported keys: 10472 * 10473 * * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a 10474 * non-positive or non-numeric value, removes the max depth limit. 10475 * Default: 5 10476 */ 10477function errorHandlingConfig(config) { 10478 if (isObject(config)) { 10479 if (isDefined(config.objectMaxDepth)) { 10480 minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN; 10481 } 10482 } else { 10483 return minErrConfig; 10484 } 10485} 10486 10487/** 10488 * @private 10489 * @param {Number} maxDepth 10490 * @return {boolean} 10491 */ 10492function isValidObjectMaxDepth(maxDepth) { 10493 return isNumber(maxDepth) && maxDepth > 0; 10494} 10495 10496/** 10497 * @ngdoc function 10498 * @name angular.lowercase 10499 * @module ng 10500 * @kind function 10501 * 10502 * @deprecated 10503 * sinceVersion="1.5.0" 10504 * removeVersion="1.7.0" 10505 * Use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) instead. 10506 * 10507 * @description Converts the specified string to lowercase. 10508 * @param {string} string String to be converted to lowercase. 10509 * @returns {string} Lowercased string. 10510 */ 10511var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; 10512 10513/** 10514 * @ngdoc function 10515 * @name angular.uppercase 10516 * @module ng 10517 * @kind function 10518 * 10519 * @deprecated 10520 * sinceVersion="1.5.0" 10521 * removeVersion="1.7.0" 10522 * Use [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) instead. 10523 * 10524 * @description Converts the specified string to uppercase. 10525 * @param {string} string String to be converted to uppercase. 10526 * @returns {string} Uppercased string. 10527 */ 10528var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; 10529 10530 10531var manualLowercase = function(s) { 10532 /* eslint-disable no-bitwise */ 10533 return isString(s) 10534 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCo
10534deAt(0) | 32);}) 10535 : s; 10536 /* eslint-enable */ 10537}; 10538var manualUppercase = function(s) { 10539 /* eslint-disable no-bitwise */ 10540 return isString(s) 10541 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) 10542 : s; 10543 /* eslint-enable */ 10544}; 10545 10546 10547// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish 10548// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods 10549// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387 10550if ('i' !== 'I'.toLowerCase()) { 10551 lowercase = manualLowercase; 10552 uppercase = manualUppercase; 10553} 10554 10555 10556var 10557 msie, // holds major version number for IE, or NaN if UA is not IE. 10558 jqLite, // delay binding since jQuery could be loaded after us. 10559 jQuery, // delay binding 10560 slice = [].slice, 10561 splice = [].splice, 10562 push = [].push, 10563 toString = Object.prototype.toString, 10564 getPrototypeOf = Object.getPrototypeOf, 10565 ngMinErr = minErr('ng'), 10566 10567 /** @name angular */ 10568 angular = window.angular || (window.angular = {}), 10569 angularModule, 10570 uid = 0; 10571 10572// Support: IE 9-11 only 10573/** 10574 * documentMode is an IE-only property 10575 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx 10576 */ 10577msie = window.document.documentMode; 10578 10579 10580/** 10581 * @private 10582 * @param {*} obj 10583 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, 10584 * String ...) 10585 */ 10586function isArrayLike(obj) { 10587 10588 // `null`, `undefined` and `window` are not array-like 10589 if (obj == null || isWindow(obj)) return false; 10590 10591 // arrays, strings and jQuery/jqLite objects are array like 10592 // * jqLite is either the jQuery or jqLite constructor function 10593 // * we have to check the existence of jqLite first as this method is called 10594 // via the forEach method when constructing the jqLite object in the first place 10595 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true; 10596 10597 // Support: iOS 8.2 (not reproducible in simulator) 10598 // "length" in obj used to prevent JIT error (gh-11508) 10599 var length = 'length' in Object(obj) && obj.length; 10600 10601 // NodeList objects (with `item` method) and 10602 // other objects with suitable length characteristics are array-like 10603 return isNumber(length) && 10604 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function'); 10605 10606} 10607 10608/** 10609 * @ngdoc function 10610 * @name angular.forEach 10611 * @module ng 10612 * @kind function 10613 * 10614 * @description 10615 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an 10616 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` 10617 * is the value of an object property or an array element, `key` is the object property key or 10618 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. 10619 * 10620 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters 10621 * using the `hasOwnProperty` method. 10622 * 10623 * Unlike ES262's 10624 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), 10625 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just 10626 * return the value provided. 10627 * 10628 ```js 10629 var values = {name: 'misko', gender: 'male'}; 10630 var log = []; 10631 angular.forEach(values, function(value, key) { 10632 this.push(key + ': ' + value); 10633 }, log); 10634 expect(log).toEqual(['name: misko', 'gender: male']); 10635 ``` 10636 * 10637 * @param {Object|Array} obj Object to iterate over. 10638 * @param {Function} iterator Iterator function. 10639 * @param {Object=} context Object to become context (`this`) for the iterator function. 10640 * @returns {Object|Array} Reference to `obj`. 10641 */ 10642 10643function forEach(obj, iterator, context) { 10644 var key, length; 10645 if (obj) { 10646 if (isFunction(obj)) { 10647 for (key in obj) { 10648 if (key !== 'prototype' && key !== 'length' && key !== 'name' && obj.hasOwnProperty(key)) { 10649 iterator.call(context, obj[key], key, obj); 10650 } 10651 } 10652 } else if (isArray(obj) || isArrayLike(obj)) { 10653 var isPrimitive = typeof obj !== 'object'; 10654 for (key = 0, length = obj.length; key < length; key++) { 10655 if (isPrimitive || key in obj) { 10656 iterator.call(context, obj[key], key, obj); 10657 } 10658 } 10659 }
vendor: 12,114 bytes, lines 10659-11160
10659 else if (obj.forEach && obj.forEach !== forEach) { 10660 obj.forEach(iterator, context, obj); 10661 } else if (isBlankObject(obj)) { 10662 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty 10663 for (key in obj) { 10664 iterator.call(context, obj[key], key, obj); 10665 } 10666 } else if (typeof obj.hasOwnProperty === 'function') { 10667 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed 10668 for (key in obj) { 10669 if (obj.hasOwnProperty(key)) { 10670 iterator.call(context, obj[key], key, obj); 10671 } 10672 } 10673 } else { 10674 // Slow path for objects which do not have a method `hasOwnProperty` 10675 for (key in obj) { 10676 if (hasOwnProperty.call(obj, key)) { 10677 iterator.call(context, obj[key], key, obj); 10678 } 10679 } 10680 } 10681 } 10682 return obj; 10683} 10684 10685function forEachSorted(obj, iterator, context) { 10686 var keys = Object.keys(obj).sort(); 10687 for (var i = 0; i < keys.length; i++) { 10688 iterator.call(context, obj[keys[i]], keys[i]); 10689 } 10690 return keys; 10691} 10692 10693 10694/** 10695 * when using forEach the params are value, key, but it is often useful to have key, value. 10696 * @param {function(string, *)} iteratorFn 10697 * @returns {function(*, string)} 10698 */ 10699function reverseParams(iteratorFn) { 10700 return function(value, key) {iteratorFn(key, value);}; 10701} 10702 10703/** 10704 * A consistent way of creating unique IDs in angular. 10705 * 10706 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before 10707 * we hit number precision issues in JavaScript. 10708 * 10709 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M 10710 * 10711 * @returns {number} an unique alpha-numeric string 10712 */ 10713function nextUid() { 10714 return ++uid; 10715} 10716 10717 10718/** 10719 * Set or clear the hashkey for an object. 10720 * @param obj object 10721 * @param h the hashkey (!truthy to delete the hashkey) 10722 */ 10723function setHashKey(obj, h) { 10724 if (h) { 10725 obj.$$hashKey = h; 10726 } else { 10727 delete obj.$$hashKey; 10728 } 10729} 10730 10731 10732function baseExtend(dst, objs, deep) { 10733 var h = dst.$$hashKey; 10734 10735 for (var i = 0, ii = objs.length; i < ii; ++i) { 10736 var obj = objs[i]; 10737 if (!isObject(obj) && !isFunction(obj)) continue; 10738 var keys = Object.keys(obj); 10739 for (var j = 0, jj = keys.length; j < jj; j++) { 10740 var key = keys[j]; 10741 var src = obj[key]; 10742 10743 if (deep && isObject(src)) { 10744 if (isDate(src)) { 10745 dst[key] = new Date(src.valueOf()); 10746 } else if (isRegExp(src)) { 10747 dst[key] = new RegExp(src); 10748 } else if (src.nodeName) { 10749 dst[key] = src.cloneNode(true); 10750 } else if (isElement(src)) { 10751 dst[key] = src.clone(); 10752 } else { 10753 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; 10754 baseExtend(dst[key], [src], true); 10755 } 10756 } else { 10757 dst[key] = src; 10758 } 10759 } 10760 } 10761 10762 setHashKey(dst, h); 10763 return dst; 10764} 10765 10766/** 10767 * @ngdoc function 10768 * @name angular.extend 10769 * @module ng 10770 * @kind function 10771 * 10772 * @description 10773 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) 10774 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so 10775 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. 10776 * 10777 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use 10778 * {@link angular.merge} for this. 10779 * 10780 * @param {Object} dst Destination object. 10781 * @param {...Object} src Source object(s). 10782 * @returns {Object} Reference to `dst`. 10783 */ 10784function extend(dst) { 10785 return baseExtend(dst, slice.call(arguments, 1), false); 10786} 10787 10788 10789/** 10790* @ngdoc function 10791* @name angular.merge 10792* @module ng 10793* @kind function 10794* 10795* @description 10796* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s) 10797* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so 10798* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`. 10799* 10800* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source 10801* objects, performing a deep copy. 10802* 10803* @param {Object} dst Destination object. 10804* @param {...Object} src Source object(s). 10805* @returns {Object} Reference to `dst`. 10806*/ 10807function merge(dst) { 10808 return baseExtend(dst, slice.call(arguments, 1), true); 10809} 10810 10811 10812 10813function toInt(str) { 10814 return parseInt(str, 10); 10815} 10816 10817var isNumberNaN = Number.isNaN || function isNumberNaN(num) { 10818 // eslint-disable-next-line no-self-compare 10819 return num !== num; 10820}; 10821 10822 10823function inherit(parent, extra) { 10824 return extend(Object.create(parent), extra); 10825} 10826 10827/** 10828 * @ngdoc function 10829 * @name angular.noop 10830 * @module ng 10831 * @kind function 10832 * 10833 * @description 10834 * A function that performs no operations. This function can be useful when writing code in the 10835 * functional style. 10836 ```js 10837 function foo(callback) { 10838 var result = calculateResult(); 10839 (callback || angular.noop)(result); 10840 } 10841 ``` 10842 */ 10843function noop() {} 10844noop.$inject = []; 10845 10846 10847/** 10848 * @ngdoc function 10849 * @name angular.identity 10850 * @module ng 10851 * @kind function 10852 * 10853 * @description 10854 * A function that returns its first argument. This function is useful when writing code in the 10855 * functional style. 10856 * 10857 ```js 10858 function transformer(transformationFn, value) { 10859 return (transformationFn || angular.identity)(value); 10860 }; 10861 10862 // E.g. 10863 function getResult(fn, input) { 10864 return (fn || angular.identity)(input); 10865 }; 10866 10867 getResult(function(n) { return n * 2; }, 21); // returns 42 10868 getResult(null, 21); // returns 21 10869 getResult(undefined, 21); // returns 21 10870 ``` 10871 * 10872 * @param {*} value to be returned. 10873 * @returns {*} the value passed in. 10874 */ 10875function identity($) {return $;} 10876identity.$inject = []; 10877 10878 10879function valueFn(value) {return function valueRef() {return value;};} 10880 10881function hasCustomToString(obj) { 10882 return isFunction(obj.toString) && obj.toString !== toString; 10883} 10884 10885 10886/** 10887 * @ngdoc function 10888 * @name angular.isUndefined 10889 * @module ng 10890 * @kind function 10891 * 10892 * @description 10893 * Determines if a reference is undefined. 10894 * 10895 * @param {*} value Reference to check. 10896 * @returns {boolean} True if `value` is undefined. 10897 */ 10898function isUndefined(value) {return typeof value === 'undefined';} 10899 10900 10901/** 10902 * @ngdoc function 10903 * @name angular.isDefined 10904 * @module ng 10905 * @kind function 10906 * 10907 * @description 10908 * Determines if a reference is defined. 10909 * 10910 * @param {*} value Reference to check. 10911 * @returns {boolean} True if `value` is defined. 10912 */ 10913function isDefined(value) {return typeof value !== 'undefined';} 10914 10915 10916/** 10917 * @ngdoc function 10918 * @name angular.isObject 10919 * @module ng 10920 * @kind function 10921 * 10922 * @description 10923 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not 10924 * considered to be objects. Note that JavaScript arrays are objects. 10925 * 10926 * @param {*} value Reference to check. 10927 * @returns {boolean} True if `value` is an `Object` but not `null`. 10928 */ 10929function isObject(value) { 10930 // http://jsperf.com/isobject4 10931 return value !== null && typeof value === 'object'; 10932} 10933 10934 10935/** 10936 * Determine if a value is an object with a null prototype 10937 * 10938 * @returns {boolean} True if `value` is an `Object` with a null prototype 10939 */ 10940function isBlankObject(value) { 10941 return value !== null && typeof value === 'object' && !getPrototypeOf(value); 10942} 10943 10944 10945/** 10946 * @ngdoc function 10947 * @name angular.isString 10948 * @module ng 10949 * @kind function 10950 * 10951 * @description 10952 * Determines if a reference is a `String`. 10953 * 10954 * @param {*} value Reference to check. 10955 * @returns {boolean} True if `value` is a `String`. 10956 */ 10957function isString(value) {return typeof value === 'string';} 10958 10959 10960/** 10961 * @ngdoc function 10962 * @name angular.isNumber 10963 * @module ng 10964 * @kind function 10965 * 10966 * @description 10967 * Determines if a reference is a `Number`. 10968 * 10969 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`. 10970 * 10971 * If you wish to exclude these then you can use the native 10972 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite) 10973 * method. 10974 * 10975 * @param {*} value Reference to check. 10976 * @returns {boolean} True if `value` is a `Number`. 10977 */ 10978function isNumber(value) {return typeof value === 'number';} 10979 10980 10981/** 10982 * @ngdoc function 10983 * @name angular.isDate 10984 * @module ng 10985 * @kind function 10986 * 10987 * @description 10988 * Determines if a value is a date. 10989 * 10990 * @param {*} value Reference to check. 10991 * @returns {boolean} True if `value` is a `Date`. 10992 */ 10993function isDate(value) { 10994 return toString.call(value) === '[object Date]'; 10995} 10996 10997 10998/** 10999 * @ngdoc function 11000 * @name angular.isArray 11001 * @module ng 11002 * @kind function 11003 * 11004 * @description 11005 * Determines if a reference is an `Array`. Alias of Array.isArray. 11006 * 11007 * @param {*} value Reference to check. 11008 * @returns {boolean} True if `value` is an `Array`. 11009 */ 11010var isArray = Array.isArray; 11011 11012/** 11013 * @ngdoc function 11014 * @name angular.isFunction 11015 * @module ng 11016 * @kind function 11017 * 11018 * @description 11019 * Determines if a reference is a `Function`. 11020 * 11021 * @param {*} value Reference to check. 11022 * @returns {boolean} True if `value` is a `Function`. 11023 */ 11024function isFunction(value) {return typeof value === 'function';} 11025 11026 11027/** 11028 * Determines if a value is a regular expression object. 11029 * 11030 * @private 11031 * @param {*} value Reference to check. 11032 * @returns {boolean} True if `value` is a `RegExp`. 11033 */ 11034function isRegExp(value) { 11035 return toString.call(value) === '[object RegExp]'; 11036} 11037 11038 11039/** 11040 * Checks if `obj` is a window object. 11041 * 11042 * @private 11043 * @param {*} obj Object to check 11044 * @returns {boolean} True if `obj` is a window obj. 11045 */ 11046function isWindow(obj) { 11047 return obj && obj.window === obj; 11048} 11049 11050 11051function isScope(obj) { 11052 return obj && obj.$evalAsync && obj.$watch; 11053} 11054 11055 11056function isFile(obj) { 11057 return toString.call(obj) === '[object File]'; 11058} 11059 11060 11061function isFormData(obj) { 11062 return toString.call(obj) === '[object FormData]'; 11063} 11064 11065 11066function isBlob(obj) { 11067 return toString.call(obj) === '[object Blob]'; 11068} 11069 11070 11071function isBoolean(value) { 11072 return typeof value === 'boolean'; 11073} 11074 11075 11076function isPromiseLike(obj) { 11077 return obj && isFunction(obj.then); 11078} 11079 11080 11081var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/; 11082function isTypedArray(value) { 11083 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value)); 11084} 11085 11086function isArrayBuffer(obj) { 11087 return toString.call(obj) === '[object ArrayBuffer]'; 11088} 11089 11090 11091var trim = function(value) { 11092 return isString(value) ? value.trim() : value; 11093}; 11094 11095// Copied from: 11096// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 11097// Prereq: s is a string. 11098var escapeForRegexp = function(s) { 11099 return s 11100 .replace(/([-()[\]{}+?*.$^|,:#<!\\])/g, '\\$1') 11101 // eslint-disable-next-line no-control-regex 11102 .replace(/\x08/g, '\\x08'); 11103}; 11104 11105 11106/** 11107 * @ngdoc function 11108 * @name angular.isElement 11109 * @module ng 11110 * @kind function 11111 * 11112 * @description 11113 * Determines if a reference is a DOM element (or wrapped jQuery element). 11114 * 11115 * @param {*} value Reference to check. 11116 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). 11117 */ 11118function isElement(node) { 11119 return !!(node && 11120 (node.nodeName // We are a direct element. 11121 || (node.prop && node.attr && node.find))); // We have an on and find method part of jQuery API. 11122} 11123 11124/** 11125 * @param str 'key1,key2,...' 11126 * @returns {object} in the form of {key1:true, key2:true, ...} 11127 */ 11128function makeMap(str) { 11129 var obj = {}, items = str.split(','), i; 11130 for (i = 0; i < items.length; i++) { 11131 obj[items[i]] = true; 11132 } 11133 return obj; 11134} 11135 11136 11137function nodeName_(element) { 11138 return lowercase(element.nodeName || (element[0] && element[0].nodeName)); 11139} 11140 11141function includes(array, obj) { 11142 return Array.prototype.indexOf.call(array, obj) !== -1; 11143} 11144 11145function arrayRemove(array, value) { 11146 var index = array.indexOf(value); 11147 if (index >= 0) { 11148 array.splice(index, 1); 11149 } 11150 return index; 11151} 11152 11153/** 11154 * @ngdoc function 11155 * @name angular.copy 11156 * @module ng 11157 * @kind function 11158 * 11159 * @description 11160 * Creates a deep copy of `source`, which should be an object or an array.
11161 * 11162 * * If no destination is supplied, a copy of the object or array is created. 11163 * * If a destination is provided, all of its elements (for arrays) or properties (for objects) 11164 * are deleted and then all elements/properties from the source are copied to it. 11165 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. 11166 * * If `source` is identical to `destination` an exception will be thrown. 11167 * 11168 * <br /> 11169 * <div class="alert alert-warning"> 11170 * Only enumerable properties are taken into account. Non-enumerable properties (both on `source` 11171 * and on `destination`) will be ignored. 11172 * </div> 11173 * 11174 * @param {*} source The source that will be used to make a copy. 11175 * Can be any type, including primitives, `null`, and `undefined`. 11176 * @param {(Object|Array)=} destination Destination into which the source is copied. If 11177 * provided, must be of the same type as `source`. 11178 * @returns {*} The copy or updated `destination`, if `destination` was specified. 11179 * 11180 * @example 11181 <example module="copyExample" name="angular-copy"> 11182 <file name="index.html">
11183 <div ng-controller="ExampleController"> 11184 <form novalidate class="simple-form"> 11185 <label>Name: <input type="text" ng-model="user.name" /></label><br /> 11186 <label>Age: <input type="number" ng-model="user.age" /></label><br /> 11187 Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label> 11188 <label><input type="radio" ng-model="user.gender" value="female" />female</label><br /> 11189 <button ng-click="reset()">RESET</button> 11190 <button ng-click="update(user)">SAVE</button> 11191 </form> 11192 <pre>form = {{user | json}}</pre> 11193 <pre>master = {{master | json}}</pre> 11194 </div> 11195 </file> 11196 <file name="script.js"> 11197 // Module: copyExample 11198 angular. 11199 module('copyExample', []). 11200 controller('ExampleController', ['$scope', function($scope) { 11201 $scope.master = {}; 11202 11203 $scope.reset = function() { 11204 // Example with 1 argument 11205 $scope.user = angular.copy($scope.master); 11206 }; 11207 11208 $scope.update = function(user) { 11209 // Example with 2 arguments 11210 angular.copy(user, $scope.master); 11211 }; 11212 11213 $scope.reset(); 11214 }]); 11215 </file> 11216 </example> 11217 */ 11218function copy(source, destination, maxDepth) { 11219 var stackSource = []; 11220 var stackDest = []; 11221 maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN; 11222 11223 if (destination) { 11224 if (isTypedArray(destination) || isArrayBuffer(destination)) { 11225 throw ngMinErr('cpta', 'Can\'t copy! TypedArray destination cannot be mutated.'); 11226 } 11227 if (source === destination) { 11228 throw ngMinErr('cpi', 'Can\'t copy! Source and destination are identical.'); 11229 } 11230 11231 // Empty the destination object 11232 if (isArray(destination)) { 11233 destination.length = 0; 11234 } else { 11235 forEach(destination, function(value, key) { 11236 if (key !== '$$hashKey') { 11237 delete destination[key]; 11238 } 11239 }); 11240 } 11241 11242 stackSource.push(source); 11243 stackDest.push(destination); 11244 return copyRecurse(source, destination, maxDepth); 11245 } 11246 11247 return copyElement(source, maxDepth); 11248 11249 function copyRecurse(source, destination, maxDepth) { 11250 maxDepth--; 11251 if (maxDepth < 0) { 11252 return '...'; 11253 } 11254 var h = destination.$$hashKey; 11255 var key; 11256 if (isArray(source)) { 11257 for (var i = 0, ii = source.length; i < ii; i++) { 11258 destination.push(copyElement(source[i], maxDepth)); 11259 } 11260 } else if (isBlankObject(source)) { 11261 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty 11262 for (key in source) { 11263 destination[key] = copyElement(source[key], maxDepth); 11264 } 11265 } else if (source && typeof source.hasOwnProperty === 'function') { 11266 // Slow path, which must rely on hasOwnProperty 11267 for (key in source) { 11268 if (source.hasOwnProperty(key)) { 11269 destination[key] = copyElement(source[key], maxDepth); 11270 } 11271 } 11272 } else { 11273 // Slowest path --- hasOwnProperty can't be called as a method 11274 for (key in source) { 11275 if (hasOwnProperty.call(source, key)) { 11276 destination[key] = copyElement(source[key], maxDepth); 11277 } 11278 } 11279 } 11280 setHashKey(destination, h); 11281 return destination; 11282 } 11283 11284 function copyElement(source, maxDepth) { 11285 // Simple values 11286 if (!isObject(source)) { 11287 return source; 11288 } 11289 11290 // Already copied values 11291 var index = stackSource.indexOf(source); 11292 if (index !== -1) { 11293 return stackDest[index]; 11294 } 11295 11296 if (isWindow(source) || isScope(source)) { 11297 throw ngMinErr('cpws', 11298 'Can\'t copy! Making copies of Window or Scope instances is not supported.'); 11299 } 11300 11301 var needsRecurse = false; 11302 var destination = copyType(source); 11303 11304 if (destination === undefined) { 11305 destination = isArray(source) ? [] : Object.create(getPrototypeOf(source)); 11306 needsRecurse = true; 11307 } 11308 11309 stackSource.push(source); 11310 stackDest.push(destination); 11311 11312 return needsRecurse 11313 ? copyRecurse(source, destination, maxDepth) 11314 : destination; 11315 } 11316 11317 function copyType(source) { 11318 switch (toString.call(source)) { 11319 case '[object Int8Array]': 11320 case '[object Int16Array]': 11321 case '[object Int32Array]': 11322 case '[object Float32Array]':
vendor: 18,304 bytes, lines 11323-11915
11323 case '[object Float64Array]': 11324 case '[object Uint8Array]': 11325 case '[object Uint8ClampedArray]': 11326 case '[object Uint16Array]': 11327 case '[object Uint32Array]': 11328 return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length); 11329 11330 case '[object ArrayBuffer]': 11331 // Support: IE10 11332 if (!source.slice) { 11333 // If we're in this case we know the environment supports ArrayBuffer 11334 /* eslint-disable no-undef */ 11335 var copied = new ArrayBuffer(source.byteLength); 11336 new Uint8Array(copied).set(new Uint8Array(source)); 11337 /* eslint-enable */ 11338 return copied; 11339 } 11340 return source.slice(0); 11341 11342 case '[object Boolean]': 11343 case '[object Number]': 11344 case '[object String]': 11345 case '[object Date]': 11346 return new source.constructor(source.valueOf()); 11347 11348 case '[object RegExp]': 11349 var re = new RegExp(source.source, source.toString().match(/[^/]*$/)[0]); 11350 re.lastIndex = source.lastIndex; 11351 return re; 11352 11353 case '[object Blob]': 11354 return new source.constructor([source], {type: source.type}); 11355 } 11356 11357 if (isFunction(source.cloneNode)) { 11358 return source.cloneNode(true); 11359 } 11360 } 11361} 11362 11363 11364/** 11365 * @ngdoc function 11366 * @name angular.equals 11367 * @module ng 11368 * @kind function 11369 * 11370 * @description 11371 * Determines if two objects or two values are equivalent. Supports value types, regular 11372 * expressions, arrays and objects. 11373 * 11374 * Two objects or values are considered equivalent if at least one of the following is true: 11375 * 11376 * * Both objects or values pass `===` comparison. 11377 * * Both objects or values are of the same type and all of their properties are equal by 11378 * comparing them with `angular.equals`. 11379 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) 11380 * * Both values represent the same regular expression (In JavaScript, 11381 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual 11382 * representation matches). 11383 * 11384 * During a property comparison, properties of `function` type and properties with names 11385 * that begin with `$` are ignored. 11386 * 11387 * Scope and DOMWindow objects are being compared only by identify (`===`). 11388 * 11389 * @param {*} o1 Object or value to compare. 11390 * @param {*} o2 Object or value to compare. 11391 * @returns {boolean} True if arguments are equal. 11392 * 11393 * @example 11394 <example module="equalsExample" name="equalsExample"> 11395 <file name="index.html"> 11396 <div ng-controller="ExampleController"> 11397 <form novalidate> 11398 <h3>User 1</h3> 11399 Name: <input type="text" ng-model="user1.name"> 11400 Age: <input type="number" ng-model="user1.age"> 11401 11402 <h3>User 2</h3> 11403 Name: <input type="text" ng-model="user2.name"> 11404 Age: <input type="number" ng-model="user2.age"> 11405 11406 <div> 11407 <br/> 11408 <input type="button" value="Compare" ng-click="compare()"> 11409 </div> 11410 User 1: <pre>{{user1 | json}}</pre> 11411 User 2: <pre>{{user2 | json}}</pre> 11412 Equal: <pre>{{result}}</pre> 11413 </form> 11414 </div> 11415 </file> 11416 <file name="script.js"> 11417 angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) { 11418 $scope.user1 = {}; 11419 $scope.user2 = {}; 11420 $scope.compare = function() { 11421 $scope.result = angular.equals($scope.user1, $scope.user2); 11422 }; 11423 }]); 11424 </file> 11425 </example> 11426 */ 11427function equals(o1, o2) { 11428 if (o1 === o2) return true; 11429 if (o1 === null || o2 === null) return false; 11430 // eslint-disable-next-line no-self-compare 11431 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN 11432 var t1 = typeof o1, t2 = typeof o2, length, key, keySet; 11433 if (t1 === t2 && t1 === 'object') { 11434 if (isArray(o1)) { 11435 if (!isArray(o2)) return false; 11436 if ((length = o1.length) === o2.length) { 11437 for (key = 0; key < length; key++) { 11438 if (!equals(o1[key], o2[key])) return false; 11439 } 11440 return true; 11441 } 11442 } else if (isDate(o1)) { 11443 if (!isDate(o2)) return false; 11444 return equals(o1.getTime(), o2.getTime()); 11445 } else if (isRegExp(o1)) { 11446 if (!isRegExp(o2)) return false; 11447 return o1.toString() === o2.toString(); 11448 } else { 11449 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || 11450 isArray(o2) || isDate(o2) || isRegExp(o2)) return false; 11451 keySet = createMap(); 11452 for (key in o1) { 11453 if (key.charAt(0) === '$' || isFunction(o1[key])) continue; 11454 if (!equals(o1[key], o2[key])) return false; 11455 keySet[key] = true; 11456 } 11457 for (key in o2) { 11458 if (!(key in keySet) && 11459 key.charAt(0) !== '$' && 11460 isDefined(o2[key]) && 11461 !isFunction(o2[key])) return false; 11462 } 11463 return true; 11464 } 11465 } 11466 return false; 11467} 11468 11469var csp = function() { 11470 if (!isDefined(csp.rules)) { 11471 11472 11473 var ngCspElement = (window.document.querySelector('[ng-csp]') || 11474 window.document.querySelector('[data-ng-csp]')); 11475 11476 if (ngCspElement) { 11477 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || 11478 ngCspElement.getAttribute('data-ng-csp'); 11479 csp.rules = { 11480 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), 11481 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) 11482 }; 11483 } else { 11484 csp.rules = { 11485 noUnsafeEval: noUnsafeEval(), 11486 noInlineStyle: false 11487 }; 11488 } 11489 } 11490 11491 return csp.rules; 11492 11493 function noUnsafeEval() { 11494 try { 11495 // eslint-disable-next-line no-new, no-new-func 11496 new Function(''); 11497 return false; 11498 } catch (e) { 11499 return true; 11500 } 11501 } 11502}; 11503 11504/** 11505 * @ngdoc directive 11506 * @module ng 11507 * @name ngJq 11508 * 11509 * @element ANY 11510 * @param {string=} ngJq the name of the library available under `window` 11511 * to be used for angular.element 11512 * @description 11513 * Use this directive to force the angular.element library. This should be 11514 * used to force either jqLite by leaving ng-jq blank or setting the name of 11515 * the jquery variable under window (eg. jQuery). 11516 * 11517 * Since angular looks for this directive when it is loaded (doesn't wait for the 11518 * DOMContentLoaded event), it must be placed on an element that comes before the script 11519 * which loads angular. Also, only the first instance of `ng-jq` will be used and all 11520 * others ignored. 11521 * 11522 * @example 11523 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag. 11524 ```html 11525 <!doctype html> 11526 <html ng-app ng-jq> 11527 ... 11528 ... 11529 </html> 11530 ``` 11531 * @example 11532 * This example shows how to use a jQuery based library of a different name. 11533 * The library name must be available at the top most 'window'. 11534 ```html 11535 <!doctype html> 11536 <html ng-app ng-jq="jQueryLib"> 11537 ... 11538 ... 11539 </html> 11540 ``` 11541 */ 11542var jq = function() { 11543 if (isDefined(jq.name_)) return jq.name_; 11544 var el; 11545 var i, ii = ngAttrPrefixes.length, prefix, name; 11546 for (i = 0; i < ii; ++i) { 11547 prefix = ngAttrPrefixes[i]; 11548 el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]'); 11549 if (el) { 11550 name = el.getAttribute(prefix + 'jq'); 11551 break; 11552 } 11553 } 11554 11555 return (jq.name_ = name); 11556}; 11557 11558function concat(array1, array2, index) { 11559 return array1.concat(slice.call(array2, index)); 11560} 11561 11562function sliceArgs(args, startIndex) { 11563 return slice.call(args, startIndex || 0); 11564} 11565 11566 11567/** 11568 * @ngdoc function 11569 * @name angular.bind 11570 * @module ng 11571 * @kind function 11572 * 11573 * @description 11574 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for 11575 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also 11576 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as 11577 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). 11578 * 11579 * @param {Object} self Context which `fn` should be evaluated in. 11580 * @param {function()} fn Function to be bound. 11581 * @param {...*} args Optional arguments to be prebound to the `fn` function call. 11582 * @returns {function()} Function that wraps the `fn` with all the specified bindings. 11583 */ 11584function bind(self, fn) { 11585 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; 11586 if (isFunction(fn) && !(fn instanceof RegExp)) { 11587 return curryArgs.length 11588 ? function() { 11589 return arguments.length 11590 ? fn.apply(self, concat(curryArgs, arguments, 0)) 11591 : fn.apply(self, curryArgs); 11592 } 11593 : function() { 11594 return arguments.length 11595 ? fn.apply(self, arguments) 11596 : fn.call(self); 11597 }; 11598 } else { 11599 // In IE, native methods are not functions so they cannot be bound (note: they don't need to be). 11600 return fn; 11601 } 11602} 11603 11604 11605function toJsonReplacer(key, value) { 11606 var val = value; 11607 11608 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { 11609 val = undefined; 11610 } else if (isWindow(value)) { 11611 val = '$WINDOW'; 11612 } else if (value && window.document === value) { 11613 val = '$DOCUMENT'; 11614 } else if (isScope(value)) { 11615 val = '$SCOPE'; 11616 } 11617 11618 return val; 11619} 11620 11621 11622/** 11623 * @ngdoc function 11624 * @name angular.toJson 11625 * @module ng 11626 * @kind function 11627 * 11628 * @description 11629 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be 11630 * stripped since angular uses this notation internally. 11631 * 11632 * @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON. 11633 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. 11634 * If set to an integer, the JSON output will contain that many spaces per indentation. 11635 * @returns {string|undefined} JSON-ified string representing `obj`. 11636 * @knownIssue 11637 * 11638 * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date` 11639 * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the 11640 * `Date.prototype.toJSON` method as follows: 11641 * 11642 * ``` 11643 * var _DatetoJSON = Date.prototype.toJSON; 11644 * Date.prototype.toJSON = function() { 11645 * try { 11646 * return _DatetoJSON.call(this); 11647 * } catch(e) { 11648 * if (e instanceof RangeError) { 11649 * return null; 11650 * } 11651 * throw e; 11652 * } 11653 * }; 11654 * ``` 11655 * 11656 * See https://github.com/angular/angular.js/pull/14221 for more information. 11657 */ 11658function toJson(obj, pretty) { 11659 if (isUndefined(obj)) return undefined; 11660 if (!isNumber(pretty)) { 11661 pretty = pretty ? 2 : null; 11662 } 11663 return JSON.stringify(obj, toJsonReplacer, pretty); 11664} 11665 11666 11667/** 11668 * @ngdoc function 11669 * @name angular.fromJson 11670 * @module ng 11671 * @kind function 11672 * 11673 * @description 11674 * Deserializes a JSON string. 11675 * 11676 * @param {string} json JSON string to deserialize. 11677 * @returns {Object|Array|string|number} Deserialized JSON string. 11678 */ 11679function fromJson(json) { 11680 return isString(json) 11681 ? JSON.parse(json) 11682 : json; 11683} 11684 11685 11686var ALL_COLONS = /:/g; 11687function timezoneToOffset(timezone, fallback) { 11688 // Support: IE 9-11 only, Edge 13-14+ 11689 // IE/Edge do not "understand" colon (`:`) in timezone 11690 timezone = timezone.replace(ALL_COLONS, ''); 11691 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; 11692 return isNumberNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; 11693} 11694 11695 11696function addDateMinutes(date, minutes) { 11697 date = new Date(date.getTime()); 11698 date.setMinutes(date.getMinutes() + minutes); 11699 return date; 11700} 11701 11702 11703function convertTimezoneToLocal(date, timezone, reverse) { 11704 reverse = reverse ? -1 : 1; 11705 var dateTimezoneOffset = date.getTimezoneOffset(); 11706 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); 11707 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); 11708} 11709 11710 11711/** 11712 * @returns {string} Returns the string representation of the element. 11713 */ 11714function startingTag(element) { 11715 element = jqLite(element).clone(); 11716 try { 11717 // turns out IE does not let you set .html() on elements which 11718 // are not allowed to have children. So we just ignore it. 11719 element.empty(); 11720 } catch (e) { /* empty */ } 11721 var elemHtml = jqLite('<div>').append(element).html(); 11722 try { 11723 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : 11724 elemHtml. 11725 match(/^(<[^>]+>)/)[1]. 11726 replace(/^<([\w-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);}); 11727 } catch (e) { 11728 return lowercase(elemHtml); 11729 } 11730 11731} 11732 11733 11734///////////////////////////////////////////////// 11735 11736/** 11737 * Tries to decode the URI component without throwing an exception. 11738 * 11739 * @private 11740 * @param str value potential URI component to check. 11741 * @returns {boolean} True if `value` can be decoded 11742 * with the decodeURIComponent function. 11743 */ 11744function tryDecodeURIComponent(value) { 11745 try { 11746 return decodeURIComponent(value); 11747 } catch (e) { 11748 // Ignore any invalid uri component. 11749 } 11750} 11751 11752 11753/** 11754 * Parses an escaped url query string into key-value pairs. 11755 * @returns {Object.<string,boolean|Array>} 11756 */ 11757function parseKeyValue(/**string*/keyValue) { 11758 var obj = {}; 11759 forEach((keyValue || '').split('&'), function(keyValue) { 11760 var splitPoint, key, val; 11761 if (keyValue) { 11762 key = keyValue = keyValue.replace(/\+/g,'%20'); 11763 splitPoint = keyValue.indexOf('='); 11764 if (splitPoint !== -1) { 11765 key = keyValue.substring(0, splitPoint); 11766 val = keyValue.substring(splitPoint + 1); 11767 } 11768 key = tryDecodeURIComponent(key); 11769 if (isDefined(key)) { 11770 val = isDefined(val) ? tryDecodeURIComponent(val) : true; 11771 if (!hasOwnProperty.call(obj, key)) { 11772 obj[key] = val; 11773 } else if (isArray(obj[key])) { 11774 obj[key].push(val); 11775 } else { 11776 obj[key] = [obj[key],val]; 11777 } 11778 } 11779 } 11780 }); 11781 return obj; 11782} 11783 11784function toKeyValue(obj) { 11785 var parts = []; 11786 forEach(obj, function(value, key) { 11787 if (isArray(value)) { 11788 forEach(value, function(arrayValue) { 11789 parts.push(encodeUriQuery(key, true) + 11790 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); 11791 }); 11792 } else { 11793 parts.push(encodeUriQuery(key, true) + 11794 (value === true ? '' : '=' + encodeUriQuery(value, true))); 11795 } 11796 }); 11797 return parts.length ? parts.join('&') : ''; 11798} 11799 11800 11801/** 11802 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow 11803 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path 11804 * segments: 11805 * segment = *pchar 11806 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 11807 * pct-encoded = "%" HEXDIG HEXDIG 11808 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 11809 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 11810 * / "*" / "+" / "," / ";" / "=" 11811 */ 11812function encodeUriSegment(val) { 11813 return encodeUriQuery(val, true). 11814 replace(/%26/gi, '&'). 11815 replace(/%3D/gi, '='). 11816 replace(/%2B/gi, '+'); 11817} 11818 11819 11820/** 11821 * This method is intended for encoding *key* or *value* parts of query component. We need a custom 11822 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be 11823 * encoded per http://tools.ietf.org/html/rfc3986: 11824 * query = *( pchar / "/" / "?" ) 11825 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 11826 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 11827 * pct-encoded = "%" HEXDIG HEXDIG 11828 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 11829 * / "*" / "+" / "," / ";" / "=" 11830 */ 11831function encodeUriQuery(val, pctEncodeSpaces) { 11832 return encodeURIComponent(val). 11833 replace(/%40/gi, '@'). 11834 replace(/%3A/gi, ':'). 11835 replace(/%24/g, '$'). 11836 replace(/%2C/gi, ','). 11837 replace(/%3B/gi, ';'). 11838 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); 11839} 11840 11841var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; 11842 11843function getNgAttribute(element, ngAttr) { 11844 var attr, i, ii = ngAttrPrefixes.length; 11845 for (i = 0; i < ii; ++i) { 11846 attr = ngAttrPrefixes[i] + ngAttr; 11847 if (isString(attr = element.getAttribute(attr))) { 11848 return attr; 11849 } 11850 } 11851 return null; 11852} 11853 11854function allowAutoBootstrap(document) { 11855 var script = document.currentScript; 11856 11857 if (!script) { 11858 // IE does not have `document.currentScript` 11859 return true; 11860 } 11861 11862 // If the `currentScript` property has been clobbered just return false, since this indicates a probable attack 11863 if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) { 11864 return false; 11865 } 11866 11867 var attributes = script.attributes; 11868 var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')]; 11869 11870 return srcs.every(function(src) { 11871 if (!src) { 11872 return true; 11873 } 11874 if (!src.value) { 11875 return false; 11876 } 11877 11878 var link = document.createElement('a'); 11879 link.href = src.value; 11880 11881 if (document.location.origin === link.origin) { 11882 // Same-origin resources are always allowed, even for non-whitelisted schemes. 11883 return true; 11884 } 11885 // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web. 11886 // This is to prevent angular.js bundled with browser extensions from being used to bypass the 11887 // content security policy in web pages and other browser extensions. 11888 switch (link.protocol) { 11889 case 'http:': 11890 case 'https:': 11891 case 'ftp:': 11892 case 'blob:': 11893 case 'file:': 11894 case 'data:': 11895 return true; 11896 default: 11897 return false; 11898 } 11899 }); 11900} 11901 11902// Cached as it has to run during loading so that document.currentScript is available. 11903var isAutoBootstrapAllowed = allowAutoBootstrap(window.document); 11904 11905/** 11906 * @ngdoc directive 11907 * @name ngApp 11908 * @module ng 11909 * 11910 * @element ANY 11911 * @param {angular.Module} ngApp an optional application 11912 * {@link angular.module module} name to load. 11913 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be 11914 * created in "strict-di" mode. This means that the application will fail to invoke functions which 11915 * do not use explicit function annotation (and are thus unsuitable for minification), as de
11915scribed 11916 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in 11917 * tracking down the root of these bugs. 11918 * 11919 * @description 11920 * 11921 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive 11922 * designates the **root element** of the application and is typically placed near the root element 11923 * of the page - e.g. on the `<body>` or `<html>` tags. 11924 * 11925 * There are a few things to keep in mind when using `ngApp`: 11926 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` 11927 * found in the document will be used to define the root element to auto-bootstrap as an 11928 * application. To run multiple applications in an HTML document you must manually bootstrap them using 11929 * {@link angular.bootstrap} instead. 11930 * - AngularJS applications cannot be nested within each other. 11931 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`. 11932 * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and 11933 * {@link ngRoute.ngView `ngView`}. 11934 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, 11935 * causing animations to stop working and making the injector inaccessible from outside the app. 11936 * 11937 * You can specify an **AngularJS module** to be used as the root module for the application. This 11938 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It 11939 * should contain the application code needed or have dependencies on other modules that will 11940 * contain the code. See {@link angular.module} for more information. 11941 * 11942 * In the example below if the `ngApp` directive were not placed on the `html` element then the 11943 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` 11944 * would not be resolved to `3`. 11945 * 11946 * `ngApp` is the easiest, and most common way to bootstrap an application. 11947 * 11948 <example module="ngAppDemo" name="ng-app"> 11949 <file name="index.html"> 11950 <div ng-controller="ngAppDemoController"> 11951 I can add: {{a}} + {{b}} = {{ a+b }} 11952 </div> 11953 </file> 11954 <file name="script.js"> 11955 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { 11956 $scope.a = 1; 11957 $scope.b = 2; 11958 }); 11959 </file> 11960 </example> 11961 * 11962 * Using `ngStrictDi`, you would see something like this: 11963 * 11964 <example ng-app-included="true" name="strict-di"> 11965 <file name="index.html"> 11966 <div ng-app="ngAppStrictDemo" ng-strict-di> 11967 <div ng-controller="GoodController1"> 11968 I can add: {{a}} + {{b}} = {{ a+b }} 11969 11970 <p>This renders because the controller does not fail to 11971 instantiate, by using explicit annotation style (see 11972 script.js for details) 11973 </p> 11974 </div> 11975 11976 <div ng-controller="GoodController2"> 11977 Name: <input ng-model="name"><br /> 11978 Hello, {{name}}! 11979 11980 <p>This renders because the controller does not fail to 11981 instantiate, by using explicit annotation style 11982 (see script.js for details) 11983 </p> 11984 </div> 11985 11986 <div ng-controller="BadController"> 11987 I can add: {{a}} + {{b}} = {{ a+b }} 11988 11989 <p>The controller could not be instantiated, due to relying 11990 on automatic function annotations (which are disabled in 11991 strict mode). As such, the content of this section is not 11992 interpolated, and there should be an error in your web console. 11993 </p> 11994 </div> 11995 </div> 11996 </file> 11997 <file name="script.js"> 11998 angular.module('ngAppStrictDemo', []) 11999 // BadController will fail to instantiate, due to relying on automatic function annotation, 12000 // rather than an explicit annotation 12001 .controller('BadController', function($scope) { 12002 $scope.a = 1; 12003 $scope.b = 2; 12004 }) 12005 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, 12006 // due to using explicit annotations using the array style and $inject property, respectively. 12007 .controller('GoodController1', ['$scope', function($scope) { 12008 $scope.a = 1; 12009 $scope.b = 2; 12010 }]) 12011 .controller('GoodController2', GoodController2); 12012 function GoodController2($scope) { 12013 $scope.name = 'World'; 12014 } 12015 GoodController2.$inject = ['$scope']; 12016 </file> 12017 <file name="style.css"> 12018 div[ng-controller] { 12019 margin-bottom: 1em; 12020 -webkit-border-radius: 4px; 12021 border-radius: 4px; 12022 border: 1px solid; 12023 padding: .5em; 12024 } 12025 div[ng-controller^=Good] { 12026 border-color: #d6e9c6; 12027 background-color: #dff0d8; 12028 color: #3c763d; 12029 } 12030 div[ng-controller^=Bad] { 12031 border-color: #ebccd1; 12032 background-color: #f2dede; 12033 color: #a94442; 12034 margin-bottom: 0; 12035 } 12036 </file> 12037 </example> 12038 */ 12039function angularInit(element, bootstrap) { 12040 var appElement, 12041 module, 12042 config = {}; 12043 12044 // The element `element` has priority over any other element. 12045 forEach(ngAttrPrefixes, function(prefix) { 12046 var name = prefix + 'app'; 12047 12048 if (!appElement && element.hasAttribute && element.hasAttribute(name)) { 12049 appElement = element; 12050 module = element.getAttribute(name); 12051 } 12052 }); 12053 forEach(ngAttrPrefixes, function(prefix) { 12054 var name = prefix + 'app'; 12055 var candidate; 12056 12057 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) { 12058 appElement = candidate; 12059 module = candidate.getAttribute(name); 12060 } 12061 }); 12062 if (appElement) { 12063 if (!isAutoBootstrapAllowed) { 12064 window.console.error('Angular: disabling automatic bootstrap. <script> protocol indicates ' + 12065 'an extension, document.location.href does not match.'); 12066 return; 12067 } 12068 config.strictDi = getNgAttribute(appElement, 'strict-di') !== null; 12069 bootstrap(appElement, module ? [module] : [], config); 12070 } 12071} 12072 12073/** 12074 * @ngdoc function 12075 * @name angular.bootstrap 12076 * @module ng 12077 * @description 12078 * Use this function to manually start up angular application. 12079 * 12080 * For more information, see the {@link guide/bootstrap Bootstrap guide}. 12081 * 12082 * Angular will detect if it has been loaded into the browser more than once and only allow the 12083 * first loaded script to be bootstrapped and will report a warning to the browser console for 12084 * each of the subsequent scripts. This prevents strange results in applications, where otherwise 12085 * multiple instances of Angular try to work on the DOM. 12086 * 12087 * <div class="alert alert-warning">
12088 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually. 12089 * They must use {@link ng.directive:ngApp ngApp}. 12090 * </div> 12091 * 12092 * <div class="alert alert-warning"> 12093 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion}, 12094 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}. 12095 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, 12096 * causing animations to stop working and making the injector inaccessible from outside the app. 12097 * </div> 12098 * 12099 * ```html 12100 * <!doctype html> 12101 * <html> 12102 * <body> 12103 * <div ng-controller="WelcomeController"> 12104 * {{greeting}} 12105 * </div> 12106 * 12107 * <script src="angular.js"></script> 12108 * <script> 12109 * var app = angular.module('demo', []) 12110 * .controller('WelcomeController', function($scope) { 12111 * $scope.greeting = 'Welcome!'; 12112 * }); 12113 * angular.bootstrap(document, ['demo']); 12114 * </script> 12115 * </body> 12116 * </html> 12117 * ``` 12118 * 12119 * @param {DOMElement} element DOM element which is the root of angular application. 12120 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application. 12121 * Each item in the array should be the name of a predefined module or a (DI annotated) 12122 * function that will be invoked by the injector as a `config` block. 12123 * See: {@link angular.module modules} 12124 * @param {Object=} config an object for defining configuration options for the application. The 12125 * following keys are supported: 12126 * 12127 * * `strictDi` - disable automatic function annotation for the application. This is meant to 12128 * assist in finding bugs which break minified code. Defaults to `false`. 12129 * 12130 * @returns {auto.$injector} Returns the newly created injector for this app. 12131 */ 12132function bootstrap(element, modules, config) { 12133 if (!isObject(config)) config = {}; 12134 var defaultConfig = { 12135 strictDi: false 12136 }; 12137 config = extend(defaultConfig, config); 12138 var doBootstrap = function() { 12139 element = jqLite(element); 12140 12141 if (element.injector()) { 12142 var tag = (element[0] === window.document) ? 'document' : startingTag(element); 12143 // Encode angle brackets to prevent input from being sanitized to empty string #8683. 12144 throw ngMinErr( 12145 'btstrpd', 12146 'App already bootstrapped with this element \'{0}\'', 12147 tag.replace(/</,'<').replace(/>/,'>')); 12148 } 12149 12150 modules = modules || []; 12151 modules.unshift(['$provide', function($provide) { 12152 $provide.value('$rootElement', element); 12153 }]); 12154 12155 if (config.debugInfoEnabled) { 12156 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. 12157 modules.push(['$compileProvider', function($compileProvider) { 12158 $compileProvider.debugInfoEnabled(true); 12159 }]); 12160 } 12161 12162 modules.unshift('ng'); 12163 var injector = createInjector(modules, config.strictDi); 12164 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', 12165 function bootstrapApply(scope, element, compile, injector) { 12166 scope.$apply(function() { 12167 element.data('$injector', injector); 12168 compile(element)(scope); 12169 }); 12170 }] 12171 ); 12172 return injector; 12173 }; 12174 12175 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/; 12176 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; 12177 12178 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) { 12179 config.debugInfoEnabled = true; 12180 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, ''); 12181 } 12182 12183 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { 12184 return doBootstrap(); 12185 } 12186 12187 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); 12188 angular.resumeBootstrap = function(extraModules) { 12189 forEach(extraModules, function(module) { 12190 modules.push(module); 12191 }); 12192 return doBootstrap(); 12193 }; 12194 12195 if (isFunction(angular.resumeDeferredBootstrap)) { 12196 angular.resumeDeferredBootstrap(); 12197 } 12198} 12199 12200/** 12201 * @ngdoc function 12202 * @name angular.reloadWithDebugInfo 12203 * @module ng 12204 * @description 12205 * Use this function to reload the current application with debug information turned on. 12206 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. 12207 * 12208 * See {@link ng.$compileProvider#debugInfoEnabled} for more. 12209 */ 12210function reloadWithDebugInfo() { 12211 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name; 12212 window.location.reload(); 12213} 12214 12215/** 12216 * @name angular.getTestability 12217 * @module ng 12218 * @description 12219 * Get the testability service for the instance of Angular on the given 12220 * element. 12221 * @param {DOMElement} element DOM element which is the root of angular application. 12222 */ 12223function getTestability(rootElement) { 12224 var injector = angular.element(rootElement).injector(); 12225 if (!injector) { 12226 throw ngMinErr('test', 12227 'no injector found for element argument to getTestability'); 12228 } 12229 return injector.get('$$testability'); 12230} 12231 12232var SNAKE_CASE_REGEXP = /[A-Z]/g; 12233function snake_case(name, separator) { 12234 separator = separator || '_'; 12235 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { 12236 return (pos ? separator : '') + letter.toLowerCase(); 12237 }); 12238} 12239 12240var bindJQueryFired = false;
vendor: 9,918 bytes, lines 12241-12557
12241function bindJQuery() { 12242 var originalCleanData; 12243 12244 if (bindJQueryFired) { 12245 return; 12246 } 12247 12248 // bind to jQuery if present; 12249 var jqName = jq(); 12250 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present) 12251 !jqName ? undefined : // use jqLite 12252 window[jqName]; // use jQuery specified by `ngJq` 12253 12254 // Use jQuery if it exists with proper functionality, otherwise default to us. 12255 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. 12256 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older 12257 // versions. It will not work for sure with jQuery <1.7, though. 12258 if (jQuery && jQuery.fn.on) { 12259 jqLite = jQuery; 12260 extend(jQuery.fn, { 12261 scope: JQLitePrototype.scope, 12262 isolateScope: JQLitePrototype.isolateScope, 12263 controller: /** @type {?} */ (JQLitePrototype).controller, 12264 injector: JQLitePrototype.injector, 12265 inheritedData: JQLitePrototype.inheritedData 12266 }); 12267 12268 // All nodes removed from the DOM via various jQuery APIs like .remove() 12269 // are passed through jQuery.cleanData. Monkey-patch this method to fire 12270 // the $destroy event on all removed nodes. 12271 originalCleanData = jQuery.cleanData; 12272 jQuery.cleanData = function(elems) { 12273 var events; 12274 for (var i = 0, elem; (elem = elems[i]) != null; i++) { 12275 events = jQuery._data(elem, 'events'); 12276 if (events && events.$destroy) { 12277 jQuery(elem).triggerHandler('$destroy'); 12278 } 12279 } 12280 originalCleanData(elems); 12281 }; 12282 } else { 12283 jqLite = JQLite; 12284 } 12285 12286 angular.element = jqLite; 12287 12288 // Prevent double-proxying. 12289 bindJQueryFired = true; 12290} 12291 12292/** 12293 * throw error if the argument is falsy. 12294 */ 12295function assertArg(arg, name, reason) { 12296 if (!arg) { 12297 throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required')); 12298 } 12299 return arg; 12300} 12301 12302function assertArgFn(arg, name, acceptArrayAnnotation) { 12303 if (acceptArrayAnnotation && isArray(arg)) { 12304 arg = arg[arg.length - 1]; 12305 } 12306 12307 assertArg(isFunction(arg), name, 'not a function, got ' + 12308 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); 12309 return arg; 12310} 12311 12312/** 12313 * throw error if the name given is hasOwnProperty 12314 * @param {String} name the name to test 12315 * @param {String} context the context in which the name is used, such as module or directive 12316 */ 12317function assertNotHasOwnProperty(name, context) { 12318 if (name === 'hasOwnProperty') { 12319 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); 12320 } 12321} 12322 12323/** 12324 * Return the value accessible from the object by path. Any undefined traversals are ignored 12325 * @param {Object} obj starting object 12326 * @param {String} path path to traverse 12327 * @param {boolean} [bindFnToScope=true] 12328 * @returns {Object} value as accessible by path 12329 */ 12330//TODO(misko): this function needs to be removed 12331function getter(obj, path, bindFnToScope) { 12332 if (!path) return obj; 12333 var keys = path.split('.'); 12334 var key; 12335 var lastInstance = obj; 12336 var len = keys.length; 12337 12338 for (var i = 0; i < len; i++) { 12339 key = keys[i]; 12340 if (obj) { 12341 obj = (lastInstance = obj)[key]; 12342 } 12343 } 12344 if (!bindFnToScope && isFunction(obj)) { 12345 return bind(lastInstance, obj); 12346 } 12347 return obj; 12348} 12349 12350/** 12351 * Return the DOM siblings between the first and last node in the given array. 12352 * @param {Array} array like object 12353 * @returns {Array} the inputted object or a jqLite collection containing the nodes 12354 */ 12355function getBlockNodes(nodes) { 12356 // TODO(perf): update `nodes` instead of creating a new object? 12357 var node = nodes[0]; 12358 var endNode = nodes[nodes.length - 1]; 12359 var blockNodes; 12360 12361 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) { 12362 if (blockNodes || nodes[i] !== node) { 12363 if (!blockNodes) { 12364 blockNodes = jqLite(slice.call(nodes, 0, i)); 12365 } 12366 blockNodes.push(node); 12367 } 12368 } 12369 12370 return blockNodes || nodes; 12371} 12372 12373 12374/** 12375 * Creates a new object without a prototype. This object is useful for lookup without having to 12376 * guard against prototypically inherited properties via hasOwnProperty. 12377 * 12378 * Related micro-benchmarks: 12379 * - http://jsperf.com/object-create2 12380 * - http://jsperf.com/proto-map-lookup/2 12381 * - http://jsperf.com/for-in-vs-object-keys2 12382 * 12383 * @returns {Object} 12384 */ 12385function createMap() { 12386 return Object.create(null); 12387} 12388 12389function stringify(value) { 12390 if (value == null) { // null || undefined 12391 return ''; 12392 } 12393 switch (typeof value) { 12394 case 'string': 12395 break; 12396 case 'number': 12397 value = '' + value; 12398 break; 12399 default: 12400 if (hasCustomToString(value) && !isArray(value) && !isDate(value)) { 12401 value = value.toString(); 12402 } else { 12403 value = toJson(value); 12404 } 12405 } 12406 12407 return value; 12408} 12409 12410var NODE_TYPE_ELEMENT = 1; 12411var NODE_TYPE_ATTRIBUTE = 2; 12412var NODE_TYPE_TEXT = 3; 12413var NODE_TYPE_COMMENT = 8; 12414var NODE_TYPE_DOCUMENT = 9; 12415var NODE_TYPE_DOCUMENT_FRAGMENT = 11; 12416 12417/** 12418 * @ngdoc type 12419 * @name angular.Module 12420 * @module ng 12421 * @description 12422 * 12423 * Interface for configuring angular {@link angular.module modules}. 12424 */ 12425 12426function setupModuleLoader(window) { 12427 12428 var $injectorMinErr = minErr('$injector'); 12429 var ngMinErr = minErr('ng'); 12430 12431 function ensure(obj, name, factory) { 12432 return obj[name] || (obj[name] = factory()); 12433 } 12434 12435 var angular = ensure(window, 'angular', Object); 12436 12437 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap 12438 angular.$$minErr = angular.$$minErr || minErr; 12439 12440 return ensure(angular, 'module', function() { 12441 /** @type {Object.<string, angular.Module>} */ 12442 var modules = {}; 12443 12444 /** 12445 * @ngdoc function 12446 * @name angular.module 12447 * @module ng 12448 * @description 12449 * 12450 * The `angular.module` is a global place for creating, registering and retrieving Angular 12451 * modules. 12452 * All modules (angular core or 3rd party) that should be available to an application must be 12453 * registered using this mechanism. 12454 * 12455 * Passing one argument retrieves an existing {@link angular.Module}, 12456 * whereas passing more than one argument creates a new {@link angular.Module} 12457 * 12458 * 12459 * # Module 12460 * 12461 * A module is a collection of services, directives, controllers, filters, and configuration information. 12462 * `angular.module` is used to configure the {@link auto.$injector $injector}. 12463 * 12464 * ```js 12465 * // Create a new module 12466 * var myModule = angular.module('myModule', []); 12467 * 12468 * // register a new service 12469 * myModule.value('appName', 'MyCoolApp'); 12470 * 12471 * // configure existing services inside initialization blocks. 12472 * myModule.config(['$locationProvider', function($locationProvider) { 12473 * // Configure existing providers 12474 * $locationProvider.hashPrefix('!'); 12475 * }]); 12476 * ``` 12477 * 12478 * Then you can create an injector and load your modules like this: 12479 * 12480 * ```js 12481 * var injector = angular.injector(['ng', 'myModule']) 12482 * ``` 12483 * 12484 * However it's more likely that you'll just use 12485 * {@link ng.directive:ngApp ngApp} or 12486 * {@link angular.bootstrap} to simplify this process for you. 12487 * 12488 * @param {!string} name The name of the module to create or retrieve. 12489 * @param {!Array.<string>=} requires If specified then new module is being created. If 12490 * unspecified then the module is being retrieved for further configuration. 12491 * @param {Function=} configFn Optional configuration function for the module. Same as 12492 * {@link angular.Module#config Module#config()}. 12493 * @returns {angular.Module} new module with the {@link angular.Module} api. 12494 */ 12495 return function module(name, requires, configFn) { 12496 12497 var info = {}; 12498 12499 var assertNotHasOwnProperty = function(name, context) { 12500 if (name === 'hasOwnProperty') { 12501 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); 12502 } 12503 }; 12504 12505 assertNotHasOwnProperty(name, 'module'); 12506 if (requires && modules.hasOwnProperty(name)) { 12507 modules[name] = null; 12508 } 12509 return ensure(modules, name, function() { 12510 if (!requires) { 12511 throw $injectorMinErr('nomod', 'Module \'{0}\' is not available! You either misspelled ' + 12512 'the module name or forgot to load it. If registering a module ensure that you ' + 12513 'specify the dependencies as the second argument.', name); 12514 } 12515 12516 /** @type {!Array.<Array.<*>>} */ 12517 var invokeQueue = []; 12518 12519 /** @type {!Array.<Function>} */ 12520 var configBlocks = []; 12521 12522 /** @type {!Array.<Function>} */ 12523 var runBlocks = []; 12524 12525 var config = invokeLater('$injector', 'invoke', 'push', configBlocks); 12526 12527 /** @type {angular.Module} */ 12528 var moduleInstance = { 12529 // Private state 12530 _invokeQueue: invokeQueue, 12531 _configBlocks: configBlocks, 12532 _runBlocks: runBlocks, 12533 12534 /** 12535 * @ngdoc method 12536 * @name angular.Module#info 12537 * @module ng 12538 * 12539 * @param {Object=} info Information about the module 12540 * @returns {Object|Module} The current info object for this module if called as a getter, 12541 * or `this` if called as a setter. 12542 * 12543 * @description 12544 * Read and write custom information about this module. 12545 * For example you could put the version of the module in here. 12546 * 12547 * ```js 12548 * angular.module('myModule', []).info({ version: '1.0.0' }); 12549 * ``` 12550 * 12551 * The version could then be read back out by accessing the module elsewhere: 12552 * 12553 * ``` 12554 * var version = angular.module('myModule').info().version; 12555 * ``` 12556 * 12557 * You can also retrieve this information during runtime via the
12558 * {@link $injector#modules `$injector.modules`} property: 12559 * 12560 * ```js 12561 * var version = $injector.modules['myModule'].info().version; 12562 * ``` 12563 */ 12564 info: function(value) { 12565 if (isDefined(value)) { 12566 if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value'); 12567 info = value; 12568 return this; 12569 } 12570 return info; 12571 }, 12572 12573 /** 12574 * @ngdoc property 12575 * @name angular.Module#requires 12576 * @module ng 12577 * 12578 * @description 12579 * Holds the list of modules which the injector will load before the current module is 12580 * loaded. 12581 */ 12582 requires: requires, 12583 12584 /** 12585 * @ngdoc property 12586 * @name angular.Module#name 12587 * @module ng 12588 * 12589 * @description 12590 * Name of the module. 12591 */ 12592 name: name, 12593 12594 12595 /** 12596 * @ngdoc method 12597 * @name angular.Module#provider 12598 * @module ng 12599 * @param {string} name service name 12600 * @param {Function} providerType Construction function for creating new instance of the 12601 * service. 12602 * @description 12603 * See {@link auto.$provide#provider $provide.provider()}. 12604 */ 12605 provider: invokeLaterAndSetModuleName('$provide', 'provider'), 12606 12607 /** 12608 * @ngdoc method 12609 * @name angular.Module#factory 12610 * @module ng 12611 * @param {string} name service name 12612 * @param {Function} providerFunction Function for creating new instance of the service. 12613 * @description 12614 * See {@link auto.$provide#factory $provide.factory()}. 12615 */ 12616 factory: invokeLaterAndSetModuleName('$provide', 'factory'), 12617 12618 /** 12619 * @ngdoc method 12620 * @name angular.Module#service 12621 * @module ng 12622 * @param {string} name service name 12623 * @param {Function} constructor A constructor function that will be instantiated. 12624 * @description 12625 * See {@link auto.$provide#service $provide.service()}. 12626 */ 12627 service: invokeLaterAndSetModuleName('$provide', 'service'), 12628 12629 /** 12630 * @ngdoc method 12631 * @name angular.Module#value 12632 * @module ng 12633 * @param {string} name service name 12634 * @param {*} object Service instance object. 12635 * @description 12636 * See {@link auto.$provide#value $provide.value()}. 12637 */ 12638 value: invokeLater('$provide', 'value'), 12639 12640 /** 12641 * @ngdoc method 12642 * @name angular.Module#constant 12643 * @module ng 12644 * @param {string} name constant name 12645 * @param {*} object Constant value. 12646 * @description 12647 * Because the constants are fixed, they get applied before other provide methods. 12648 * See {@link auto.$provide#constant $provide.constant()}. 12649 */ 12650 constant: invokeLater('$provide', 'constant', 'unshift'), 12651 12652 /** 12653 * @ngdoc method 12654 * @name angular.Module#decorator 12655 * @module ng 12656 * @param {string} name The name of the service to decorate. 12657 * @param {Function} decorFn This function will be invoked when the service needs to be 12658 * instantiated and should return the decorated service instance. 12659 * @description 12660 * See {@link auto.$provide#decorator $provide.decorator()}. 12661 */ 12662 decorator: invokeLaterAndSetModuleName('$provide', 'decorator', configBlocks), 12663 12664 /** 12665 * @ngdoc method 12666 * @name angular.Module#animation 12667 * @module ng 12668 * @param {string} name animation name 12669 * @param {Function} animationFactory Factory function for creating new instance of an 12670 * animation.
12671 * @description 12672 * 12673 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. 12674 * 12675 * 12676 * Defines an animation hook that can be later used with 12677 * {@link $animate $animate} service and directives that use this service. 12678 * 12679 * ```js 12680 * module.animation('.animation-name', function($inject1, $inject2) { 12681 * return { 12682 * eventName : function(element, done) { 12683 * //code to run the animation 12684 * //once complete, then run done() 12685 * return function cancellationFunction(element) { 12686 * //code to cancel the animation 12687 * } 12688 * } 12689 * } 12690 * }) 12691 * ``` 12692 * 12693 * See {@link ng.$animateProvider#register $animateProvider.register()} and 12694 * {@link ngAnimate ngAnimate module} for more information. 12695 */ 12696 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'), 12697 12698 /** 12699 * @ngdoc method 12700 * @name angular.Module#filter 12701 * @module ng 12702 * @param {string} name Filter name - this must be a valid angular expression identifier 12703 * @param {Function} filterFactory Factory function for creating new instance of filter. 12704 * @description 12705 * See {@link ng.$filterProvider#register $filterProvider.register()}. 12706 * 12707 * <div class="alert alert-warning"> 12708 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. 12709 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace 12710 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores 12711 * (`myapp_subsection_filterx`). 12712 * </div> 12713 */ 12714 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'), 12715 12716 /** 12717 * @ngdoc method 12718 * @name angular.Module#controller 12719 * @module ng 12720 * @param {string|Object} name Controller name, or an object map of controllers where the 12721 * keys are the names and the values are the constructors. 12722 * @param {Function} constructor Controller constructor function. 12723 * @description 12724 * See {@link ng.$controllerProvider#register $controllerProvider.register()}. 12725 */ 12726 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'), 12727 12728 /** 12729 * @ngdoc method 12730 * @name angular.Module#directive 12731 * @module ng 12732 * @param {string|Object} name Directive name, or an object map of directives where the 12733 * keys are the names and the values are the factories. 12734 * @param {Function} directiveFactory Factory function for creating new instance of 12735 * directives. 12736 * @description 12737 * See {@link ng.$compileProvider#directive $compileProvider.directive()}. 12738 */ 12739 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'), 12740 12741 /** 12742 * @ngdoc method 12743 * @name angular.Module#component 12744 * @module ng 12745 * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp) 12746 * @param {Object} options Component definition object (a simplified 12747 * {@link ng.$compile#directive-definition-object directive definition object}) 12748 * 12749 * @description 12750 * See {@link ng.$compileProvider#component $compileProvider.component()}. 12751 */ 12752 component: invokeLaterAndSetModuleName('$compileProvider', 'component'), 12753 12754 /** 12755 * @ngdoc method 12756 * @name angular.Module#config 12757 * @module ng 12758 * @param {Function} configFn Execute this function on module load. Useful for service 12759 * configuration. 12760 * @description 12761 * Use this method to register work which needs to be performed on module loading. 12762 * For more about how to configure services, see 12763 * {@link providers#provider-recipe Provider Recipe}. 12764 */ 12765 config: config, 12766 12767 /** 12768 * @ngdoc method 12769 * @name angular.Module#run 12770 * @module ng 12771 * @param {Function} initializationFn Execute this function after injector creation. 12772 * Useful for application initialization.
vendor: 9,201 bytes, lines 12773-13107
12773 * @description 12774 * Use this method to register work which should be performed when the injector is done 12775 * loading all modules. 12776 */ 12777 run: function(block) { 12778 runBlocks.push(block); 12779 return this; 12780 } 12781 }; 12782 12783 if (configFn) { 12784 config(configFn); 12785 } 12786 12787 return moduleInstance; 12788 12789 /** 12790 * @param {string} provider 12791 * @param {string} method 12792 * @param {String=} insertMethod 12793 * @returns {angular.Module} 12794 */ 12795 function invokeLater(provider, method, insertMethod, queue) { 12796 if (!queue) queue = invokeQueue; 12797 return function() { 12798 queue[insertMethod || 'push']([provider, method, arguments]); 12799 return moduleInstance; 12800 }; 12801 } 12802 12803 /** 12804 * @param {string} provider 12805 * @param {string} method 12806 * @returns {angular.Module} 12807 */ 12808 function invokeLaterAndSetModuleName(provider, method, queue) { 12809 if (!queue) queue = invokeQueue; 12810 return function(recipeName, factoryFunction) { 12811 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name; 12812 queue.push([provider, method, arguments]); 12813 return moduleInstance; 12814 }; 12815 } 12816 }); 12817 }; 12818 }); 12819 12820} 12821 12822/* global shallowCopy: true */ 12823 12824/** 12825 * Creates a shallow copy of an object, an array or a primitive. 12826 * 12827 * Assumes that there are no proto properties for objects. 12828 */ 12829function shallowCopy(src, dst) { 12830 if (isArray(src)) { 12831 dst = dst || []; 12832 12833 for (var i = 0, ii = src.length; i < ii; i++) { 12834 dst[i] = src[i]; 12835 } 12836 } else if (isObject(src)) { 12837 dst = dst || {}; 12838 12839 for (var key in src) { 12840 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { 12841 dst[key] = src[key]; 12842 } 12843 } 12844 } 12845 12846 return dst || src; 12847} 12848 12849/* global toDebugString: true */ 12850 12851function serializeObject(obj, maxDepth) { 12852 var seen = []; 12853 12854 // There is no direct way to stringify object until reaching a specific depth 12855 // and a very deep object can cause a performance issue, so we copy the object 12856 // based on this specific depth and then stringify it. 12857 if (isValidObjectMaxDepth(maxDepth)) { 12858 obj = copy(obj, null, maxDepth); 12859 } 12860 return JSON.stringify(obj, function(key, val) { 12861 val = toJsonReplacer(key, val); 12862 if (isObject(val)) { 12863 12864 if (seen.indexOf(val) >= 0) return '...'; 12865 12866 seen.push(val); 12867 } 12868 return val; 12869 }); 12870} 12871 12872function toDebugString(obj, maxDepth) { 12873 if (typeof obj === 'function') { 12874 return obj.toString().replace(/ \{[\s\S]*$/, ''); 12875 } else if (isUndefined(obj)) { 12876 return 'undefined'; 12877 } else if (typeof obj !== 'string') { 12878 return serializeObject(obj, maxDepth); 12879 } 12880 return obj; 12881} 12882 12883/* global angularModule: true, 12884 version: true, 12885 12886 $CompileProvider, 12887 12888 htmlAnchorDirective, 12889 inputDirective, 12890 inputDirective, 12891 formDirective, 12892 scriptDirective, 12893 selectDirective, 12894 optionDirective, 12895 ngBindDirective, 12896 ngBindHtmlDirective, 12897 ngBindTemplateDirective, 12898 ngClassDirective, 12899 ngClassEvenDirective, 12900 ngClassOddDirective, 12901 ngCloakDirective, 12902 ngControllerDirective, 12903 ngFormDirective, 12904 ngHideDirective, 12905 ngIfDirective, 12906 ngIncludeDirective, 12907 ngIncludeFillContentDirective, 12908 ngInitDirective, 12909 ngNonBindableDirective, 12910 ngPluralizeDirective, 12911 ngRepeatDirective, 12912 ngShowDirective, 12913 ngStyleDirective, 12914 ngSwitchDirective, 12915 ngSwitchWhenDirective, 12916 ngSwitchDefaultDirective, 12917 ngOptionsDirective, 12918 ngTranscludeDirective, 12919 ngModelDirective, 12920 ngListDirective, 12921 ngChangeDirective, 12922 patternDirective, 12923 patternDirective, 12924 requiredDirective, 12925 requiredDirective, 12926 minlengthDirective, 12927 minlengthDirective, 12928 maxlengthDirective, 12929 maxlengthDirective, 12930 ngValueDirective, 12931 ngModelOptionsDirective, 12932 ngAttributeAliasDirectives, 12933 ngEventDirectives, 12934 12935 $AnchorScrollProvider, 12936 $AnimateProvider, 12937 $CoreAnimateCssProvider, 12938 $$CoreAnimateJsProvider, 12939 $$CoreAnimateQueueProvider, 12940 $$AnimateRunnerFactoryProvider, 12941 $$AnimateAsyncRunFactoryProvider, 12942 $BrowserProvider, 12943 $CacheFactoryProvider, 12944 $ControllerProvider, 12945 $DateProvider, 12946 $DocumentProvider, 12947 $$IsDocumentHiddenProvider, 12948 $ExceptionHandlerProvider, 12949 $FilterProvider, 12950 $$ForceReflowProvider, 12951 $InterpolateProvider, 12952 $IntervalProvider, 12953 $HttpProvider, 12954 $HttpParamSerializerProvider, 12955 $HttpParamSerializerJQLikeProvider, 12956 $HttpBackendProvider, 12957 $xhrFactoryProvider, 12958 $jsonpCallbacksProvider, 12959 $LocationProvider, 12960 $LogProvider, 12961 $$MapProvider, 12962 $ParseProvider, 12963 $RootScopeProvider, 12964 $QProvider, 12965 $$QProvider, 12966 $$SanitizeUriProvider, 12967 $SceProvider, 12968 $SceDelegateProvider, 12969 $SnifferProvider, 12970 $TemplateCacheProvider, 12971 $TemplateRequestProvider, 12972 $$TestabilityProvider, 12973 $TimeoutProvider, 12974 $$RAFProvider, 12975 $WindowProvider, 12976 $$jqLiteProvider, 12977 $$CookieReaderProvider 12978*/ 12979 12980 12981/** 12982 * @ngdoc object 12983 * @name angular.version 12984 * @module ng 12985 * @description 12986 * An object that contains information about the current AngularJS version. 12987 * 12988 * This object has the following properties: 12989 * 12990 * - `full` – `{string}` – Full version string, such as "0.9.18". 12991 * - `major` – `{number}` – Major version number, such as "0". 12992 * - `minor` – `{number}` – Minor version number, such as "9". 12993 * - `dot` – `{number}` – Dot version number, such as "18". 12994 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". 12995 */ 12996var version = { 12997 // These placeholder strings will be replaced by grunt's `build` task. 12998 // They need to be double- or single-quoted. 12999 full: '1.6.3', 13000 major: 1, 13001 minor: 6, 13002 dot: 3, 13003 codeName: 'scriptalicious-bootstrapping' 13004}; 13005 13006 13007function publishExternalAPI(angular) { 13008 extend(angular, { 13009 'errorHandlingConfig': errorHandlingConfig, 13010 'bootstrap': bootstrap, 13011 'copy': copy, 13012 'extend': extend, 13013 'merge': merge, 13014 'equals': equals, 13015 'element': jqLite, 13016 'forEach': forEach, 13017 'injector': createInjector, 13018 'noop': noop, 13019 'bind': bind, 13020 'toJson': toJson, 13021 'fromJson': fromJson, 13022 'identity': identity, 13023 'isUndefined': isUndefined, 13024 'isDefined': isDefined, 13025 'isString': isString, 13026 'isFunction': isFunction, 13027 'isObject': isObject, 13028 'isNumber': isNumber, 13029 'isElement': isElement, 13030 'isArray': isArray, 13031 'version': version, 13032 'isDate': isDate, 13033 'lowercase': lowercase, 13034 'uppercase': uppercase, 13035 'callbacks': {$$counter: 0}, 13036 'getTestability': getTestability, 13037 'reloadWithDebugInfo': reloadWithDebugInfo, 13038 '$$minErr': minErr, 13039 '$$csp': csp, 13040 '$$encodeUriSegment': encodeUriSegment, 13041 '$$encodeUriQuery': encodeUriQuery, 13042 '$$stringify': stringify 13043 }); 13044 13045 angularModule = setupModuleLoader(window); 13046 13047 angularModule('ng', ['ngLocale'], ['$provide', 13048 function ngModule($provide) { 13049 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. 13050 $provide.provider({ 13051 $$sanitizeUri: $$SanitizeUriProvider 13052 }); 13053 $provide.provider('$compile', $CompileProvider). 13054 directive({ 13055 a: htmlAnchorDirective, 13056 input: inputDirective, 13057 textarea: inputDirective, 13058 form: formDirective, 13059 script: scriptDirective, 13060 select: selectDirective, 13061 option: optionDirective, 13062 ngBind: ngBindDirective, 13063 ngBindHtml: ngBindHtmlDirective, 13064 ngBindTemplate: ngBindTemplateDirective, 13065 ngClass: ngClassDirective, 13066 ngClassEven: ngClassEvenDirective, 13067 ngClassOdd: ngClassOddDirective, 13068 ngCloak: ngCloakDirective, 13069 ngController: ngControllerDirective, 13070 ngForm: ngFormDirective, 13071 ngHide: ngHideDirective, 13072 ngIf: ngIfDirective, 13073 ngInclude: ngIncludeDirective, 13074 ngInit: ngInitDirective, 13075 ngNonBindable: ngNonBindableDirective, 13076 ngPluralize: ngPluralizeDirective, 13077 ngRepeat: ngRepeatDirective, 13078 ngShow: ngShowDirective, 13079 ngStyle: ngStyleDirective, 13080 ngSwitch: ngSwitchDirective, 13081 ngSwitchWhen: ngSwitchWhenDirective, 13082 ngSwitchDefault: ngSwitchDefaultDirective, 13083 ngOptions: ngOptionsDirective, 13084 ngTransclude: ngTranscludeDirective, 13085 ngModel: ngModelDirective, 13086 ngList: ngListDirective, 13087 ngChange: ngChangeDirective, 13088 pattern: patternDirective, 13089 ngPattern: patternDirective, 13090 required: requiredDirective, 13091 ngRequired: requiredDirective, 13092 minlength: minlengthDirective, 13093 ngMinlength: minlengthDirective, 13094 maxlength: maxlengthDirective, 13095 ngMaxlength: maxlengthDirective, 13096 ngValue: ngValueDirective, 13097 ngModelOptions: ngModelOptionsDirective 13098 }). 13099 directive({ 13100 ngInclude: ngIncludeFillContentDirective 13101 }). 13102 directive(ngAttributeAliasDirectives). 13103 directive(ngEventDirectives); 13104 $provide.provider({ 13105 $anchorScroll: $AnchorScrollProvider, 13106 $animate: $AnimateProvider, 13107 $animateCss: $CoreAnimateCssProvider,
13108 $$animateJs: $$CoreAnimateJsProvider, 13109 $$animateQueue: $$CoreAnimateQueueProvider, 13110 $$AnimateRunner: $$AnimateRunnerFactoryProvider, 13111 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider, 13112 $browser: $BrowserProvider, 13113 $cacheFactory: $CacheFactoryProvider, 13114 $controller: $ControllerProvider, 13115 $document: $DocumentProvider, 13116 $$isDocumentHidden: $$IsDocumentHiddenProvider, 13117 $exceptionHandler: $ExceptionHandlerProvider, 13118 $filter: $FilterProvider, 13119 $$forceReflow: $$ForceReflowProvider, 13120 $interpolate: $InterpolateProvider, 13121 $interval: $IntervalProvider, 13122 $http: $HttpProvider, 13123 $httpParamSerializer: $HttpParamSerializerProvider, 13124 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider, 13125 $httpBackend: $HttpBackendProvider, 13126 $xhrFactory: $xhrFactoryProvider, 13127 $jsonpCallbacks: $jsonpCallbacksProvider, 13128 $location: $LocationProvider, 13129 $log: $LogProvider, 13130 $parse: $ParseProvider, 13131 $rootScope: $RootScopeProvider, 13132 $q: $QProvider, 13133 $$q: $$QProvider, 13134 $sce: $SceProvider, 13135 $sceDelegate: $SceDelegateProvider, 13136 $sniffer: $SnifferProvider, 13137 $templateCache: $TemplateCacheProvider, 13138 $templateRequest: $TemplateRequestProvider, 13139 $$testability: $$TestabilityProvider, 13140 $timeout: $TimeoutProvider, 13141 $window: $WindowProvider, 13142 $$rAF: $$RAFProvider, 13143 $$jqLite: $$jqLiteProvider, 13144 $$Map: $$MapProvider, 13145 $$cookieReader: $$CookieReaderProvider 13146 }); 13147 } 13148 ]) 13149 .info({ angularVersion: '1.6.3' }); 13150} 13151 13152/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 13153 * Any commits to this file should be reviewed with security in mind. * 13154 * Changes to this file can potentially create security vulnerabilities. * 13155 * An approval from 2 Core members with history of modifying * 13156 * this file is required. * 13157 * * 13158 * Does the change somehow allow for arbitrary javascript to be executed? * 13159 * Or allows for someone to change the prototype of built-in objects? * 13160 * Or gives undesired access to variables likes document or window? * 13161 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 13162 13163/* global 13164 JQLitePrototype: true, 13165 BOOLEAN_ATTR: true, 13166 ALIASED_ATTR: true 13167*/ 13168 13169////////////////////////////////// 13170//JQLite 13171////////////////////////////////// 13172 13173/** 13174 * @ngdoc function 13175 * @name angular.element 13176 * @module ng 13177 * @kind function 13178 * 13179 * @description 13180 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. 13181 * 13182 * If jQuery is available, `angular.element` is an alias for the 13183 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` 13184 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**. 13185 * 13186 * jqLite is a tiny, API-compatible subset of jQuery that allows 13187 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most 13188 * commonly needed functionality with the goal of having a very small footprint. 13189 * 13190 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the 13191 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a 13192 * specific version of jQuery if multiple versions exist on the page. 13193 * 13194 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or 13195 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div> 13196 * 13197 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements 13198 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)` 13199 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div> 13200 * 13201 * ## Angular's jqLite 13202 * jqLite provides only the following jQuery methods: 13203 * 13204 * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument 13205 * - [`after()`](http://api.jquery.com/after/) 13206 * - [`append()`](http://api.jquery.com/append/) 13207 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters 13208 * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData 13209 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors 13210 * - [`clone()`](http://api.jquery.com/clone/) 13211 * - [`contents()`](http://api.jquery.com/contents/) 13212 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
13213 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing. 13214 * - [`data()`](http://api.jquery.com/data/) 13215 * - [`detach()`](http://api.jquery.com/detach/) 13216 * - [`empty()`](http://api.jquery.com/empty/) 13217 * - [`eq()`](http://api.jquery.com/eq/) 13218 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name 13219 * - [`hasClass()`](http://api.jquery.com/hasClass/) 13220 * - [`html()`](http://api.jquery.com/html/) 13221 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors 13222 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData 13223 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter 13224 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors 13225 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors 13226 * - [`prepend()`](http://api.jquery.com/prepend/) 13227 * - [`prop()`](http://api.jquery.com/prop/) 13228 * - [`ready()`](http://api.jquery.com/ready/) (_deprecated_, use `angular.element(callback)` instead of `angular.element(document).ready(callback)`) 13229 * - [`remove()`](http://api.jquery.com/remove/) 13230 * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes 13231 * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument 13232 * - [`removeData()`](http://api.jquery.com/removeData/) 13233 * - [`replaceWith()`](http://api.jquery.com/replaceWith/) 13234 * - [`text()`](http://api.jquery.com/text/) 13235 * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument 13236 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers 13237 * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter 13238 * - [`val()`](http://api.jquery.com/val/) 13239 * - [`wrap()`](http://api.jquery.com/wrap/) 13240 * 13241 * ## jQuery/jqLite Extras 13242 * Angular also provides the following additional methods and events to both jQuery and jqLite: 13243 * 13244 * ### Events 13245 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event 13246 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM 13247 * element before it is removed. 13248 * 13249 * ### Methods 13250 * - `controller(name)` - retrieves the controller of the current element or its parent. By default 13251 * retrieves controller associated with the `ngController` directive. If `name` is provided as 13252 * camelCase directive name, then the controller for this directive will be retrieved (e.g. 13253 * `'ngModel'`). 13254 * - `injector()` - retrieves the injector of the current element or its parent. 13255 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current 13256 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to 13257 * be enabled. 13258 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the 13259 * current element. This getter should be used only on elements that contain a directive which starts a new isolate 13260 * scope. Calling `scope()` on this element always returns the original non-isolate scope. 13261 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. 13262 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top 13263 * parent element is reached. 13264 * 13265 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See 13266 * https://github.com/angular/angular.js/issues/14251 for more information. 13267 * 13268 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. 13269 * @returns {Object} jQuery object. 13270 */ 13271 13272JQLite.expando = 'ng339'; 13273 13274var jqCache = JQLite.cache = {}, 13275 jqId = 1; 13276 13277/* 13278 * !!! This is an undocumented "private" function !!! 13279 */ 13280JQLite._data = function(node) { 13281 //jQuery always returns an object on cache miss 13282 return this.cache[node[this.expando]] || {}; 13283}; 13284 13285function jqNextId() { return ++jqId; } 13286 13287 13288var DASH_LOWERCASE_REGEXP = /-([a-z])/g; 13289var MS_HACK_REGEXP = /^-ms-/; 13290var MOUSE_EVENT_MAP = { mouseleave: 'mouseout', mouseenter: 'mouseover' }; 13291var jqLiteMinErr = minErr('jqLite'); 13292 13293/** 13294 * Converts kebab-case to camelCase. 13295 * There is also a special case for the ms prefix starting with a lowercase letter. 13296 * @param name Name to normalize 13297 */ 13298function cssKebabToCamel(name) { 13299 return kebabToCamel(name.replace(MS_HACK_REGEXP, 'ms-')); 13300} 13301 13302function fnCamelCaseReplace(all, letter) { 13303 return letter.toUpperCase(); 13304} 13305 13306/** 13307 * Converts kebab-case to camelCase. 13308 * @param name Name to normalize 13309 */ 13310function kebabToCamel(name) { 13311 return name 13312 .replace(DASH_LOWERCASE_REGEXP, fnCamelCaseReplace); 13313} 13314 13315var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
vendor: 12,520 bytes, lines 13316-13765
13316var HTML_REGEXP = /<|&#?\w+;/; 13317var TAG_NAME_REGEXP = /<([\w:-]+)/; 13318var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi; 13319 13320var wrapMap = { 13321 'option': [1, '<select multiple="multiple">', '</select>'], 13322 13323 'thead': [1, '<table>', '</table>'], 13324 'col': [2, '<table><colgroup>', '</colgroup></table>'], 13325 'tr': [2, '<table><tbody>', '</tbody></table>'], 13326 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'], 13327 '_default': [0, '', ''] 13328}; 13329 13330wrapMap.optgroup = wrapMap.option; 13331wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; 13332wrapMap.th = wrapMap.td; 13333 13334 13335function jqLiteIsTextNode(html) { 13336 return !HTML_REGEXP.test(html); 13337} 13338 13339function jqLiteAcceptsData(node) { 13340 // The window object can accept data but has no nodeType 13341 // Otherwise we are only interested in elements (1) and documents (9) 13342 var nodeType = node.nodeType; 13343 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT; 13344} 13345 13346function jqLiteHasData(node) { 13347 for (var key in jqCache[node.ng339]) { 13348 return true; 13349 } 13350 return false; 13351} 13352 13353function jqLiteCleanData(nodes) { 13354 for (var i = 0, ii = nodes.length; i < ii; i++) { 13355 jqLiteRemoveData(nodes[i]); 13356 } 13357} 13358 13359function jqLiteBuildFragment(html, context) { 13360 var tmp, tag, wrap, 13361 fragment = context.createDocumentFragment(), 13362 nodes = [], i; 13363 13364 if (jqLiteIsTextNode(html)) { 13365 // Convert non-html into a text node 13366 nodes.push(context.createTextNode(html)); 13367 } else { 13368 // Convert html into DOM nodes 13369 tmp = fragment.appendChild(context.createElement('div')); 13370 tag = (TAG_NAME_REGEXP.exec(html) || ['', ''])[1].toLowerCase(); 13371 wrap = wrapMap[tag] || wrapMap._default; 13372 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, '<$1></$2>') + wrap[2]; 13373 13374 // Descend through wrappers to the right content 13375 i = wrap[0]; 13376 while (i--) { 13377 tmp = tmp.lastChild; 13378 } 13379 13380 nodes = concat(nodes, tmp.childNodes); 13381 13382 tmp = fragment.firstChild; 13383 tmp.textContent = ''; 13384 } 13385 13386 // Remove wrapper from fragment 13387 fragment.textContent = ''; 13388 fragment.innerHTML = ''; // Clear inner HTML 13389 forEach(nodes, function(node) { 13390 fragment.appendChild(node); 13391 }); 13392 13393 return fragment; 13394} 13395 13396function jqLiteParseHTML(html, context) { 13397 context = context || window.document; 13398 var parsed; 13399 13400 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { 13401 return [context.createElement(parsed[1])]; 13402 } 13403 13404 if ((parsed = jqLiteBuildFragment(html, context))) { 13405 return parsed.childNodes; 13406 } 13407 13408 return []; 13409} 13410 13411function jqLiteWrapNode(node, wrapper) { 13412 var parent = node.parentNode; 13413 13414 if (parent) { 13415 parent.replaceChild(wrapper, node); 13416 } 13417 13418 wrapper.appendChild(node); 13419} 13420 13421 13422// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. 13423var jqLiteContains = window.Node.prototype.contains || /** @this */ function(arg) { 13424 // eslint-disable-next-line no-bitwise 13425 return !!(this.compareDocumentPosition(arg) & 16); 13426}; 13427 13428///////////////////////////////////////////// 13429function JQLite(element) { 13430 if (element instanceof JQLite) { 13431 return element; 13432 } 13433 13434 var argIsString; 13435 13436 if (isString(element)) { 13437 element = trim(element); 13438 argIsString = true; 13439 } 13440 if (!(this instanceof JQLite)) { 13441 if (argIsString && element.charAt(0) !== '<') { 13442 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); 13443 } 13444 return new JQLite(element); 13445 } 13446 13447 if (argIsString) { 13448 jqLiteAddNodes(this, jqLiteParseHTML(element)); 13449 } else if (isFunction(element)) { 13450 jqLiteReady(element); 13451 } else { 13452 jqLiteAddNodes(this, element); 13453 } 13454} 13455 13456function jqLiteClone(element) { 13457 return element.cloneNode(true); 13458} 13459 13460function jqLiteDealoc(element, onlyDescendants) { 13461 if (!onlyDescendants) jqLiteRemoveData(element); 13462 13463 if (element.querySelectorAll) { 13464 var descendants = element.querySelectorAll('*'); 13465 for (var i = 0, l = descendants.length; i < l; i++) { 13466 jqLiteRemoveData(descendants[i]); 13467 } 13468 } 13469} 13470 13471function jqLiteOff(element, type, fn, unsupported) { 13472 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); 13473 13474 var expandoStore = jqLiteExpandoStore(element); 13475 var events = expandoStore && expandoStore.events; 13476 var handle = expandoStore && expandoStore.handle; 13477 13478 if (!handle) return; //no listeners registered 13479 13480 if (!type) { 13481 for (type in events) { 13482 if (type !== '$destroy') { 13483 element.removeEventListener(type, handle); 13484 } 13485 delete events[type]; 13486 } 13487 } else { 13488 13489 var removeHandler = function(type) { 13490 var listenerFns = events[type]; 13491 if (isDefined(fn)) { 13492 arrayRemove(listenerFns || [], fn); 13493 } 13494 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) { 13495 element.removeEventListener(type, handle); 13496 delete events[type]; 13497 } 13498 }; 13499 13500 forEach(type.split(' '), function(type) { 13501 removeHandler(type); 13502 if (MOUSE_EVENT_MAP[type]) { 13503 removeHandler(MOUSE_EVENT_MAP[type]); 13504 } 13505 }); 13506 } 13507} 13508 13509function jqLiteRemoveData(element, name) { 13510 var expandoId = element.ng339; 13511 var expandoStore = expandoId && jqCache[expandoId]; 13512 13513 if (expandoStore) { 13514 if (name) { 13515 delete expandoStore.data[name]; 13516 return; 13517 } 13518 13519 if (expandoStore.handle) { 13520 if (expandoStore.events.$destroy) { 13521 expandoStore.handle({}, '$destroy'); 13522 } 13523 jqLiteOff(element); 13524 } 13525 delete jqCache[expandoId]; 13526 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it 13527 } 13528} 13529 13530 13531function jqLiteExpandoStore(element, createIfNecessary) { 13532 var expandoId = element.ng339, 13533 expandoStore = expandoId && jqCache[expandoId]; 13534 13535 if (createIfNecessary && !expandoStore) { 13536 element.ng339 = expandoId = jqNextId(); 13537 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined}; 13538 } 13539 13540 return expandoStore; 13541} 13542 13543 13544function jqLiteData(element, key, value) { 13545 if (jqLiteAcceptsData(element)) { 13546 var prop; 13547 13548 var isSimpleSetter = isDefined(value); 13549 var isSimpleGetter = !isSimpleSetter && key && !isObject(key); 13550 var massGetter = !key; 13551 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter); 13552 var data = expandoStore && expandoStore.data; 13553 13554 if (isSimpleSetter) { // data('key', value) 13555 data[kebabToCamel(key)] = value; 13556 } else { 13557 if (massGetter) { // data() 13558 return data; 13559 } else { 13560 if (isSimpleGetter) { // data('key') 13561 // don't force creation of expandoStore if it doesn't exist yet 13562 return data && data[kebabToCamel(key)]; 13563 } else { // mass-setter: data({key1: val1, key2: val2}) 13564 for (prop in key) { 13565 data[kebabToCamel(prop)] = key[prop]; 13566 } 13567 } 13568 } 13569 } 13570 } 13571} 13572 13573function jqLiteHasClass(element, selector) { 13574 if (!element.getAttribute) return false; 13575 return ((' ' + (element.getAttribute('class') || '') + ' ').replace(/[\n\t]/g, ' '). 13576 indexOf(' ' + selector + ' ') > -1); 13577} 13578 13579function jqLiteRemoveClass(element, cssClasses) { 13580 if (cssClasses && element.setAttribute) { 13581 forEach(cssClasses.split(' '), function(cssClass) { 13582 element.setAttribute('class', trim( 13583 (' ' + (element.getAttribute('class') || '') + ' ') 13584 .replace(/[\n\t]/g, ' ') 13585 .replace(' ' + trim(cssClass) + ' ', ' ')) 13586 ); 13587 }); 13588 } 13589} 13590 13591function jqLiteAddClass(element, cssClasses) { 13592 if (cssClasses && element.setAttribute) { 13593 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') 13594 .replace(/[\n\t]/g, ' '); 13595 13596 forEach(cssClasses.split(' '), function(cssClass) { 13597 cssClass = trim(cssClass); 13598 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { 13599 existingClasses += cssClass + ' '; 13600 } 13601 }); 13602 13603 element.setAttribute('class', trim(existingClasses)); 13604 } 13605} 13606 13607 13608function jqLiteAddNodes(root, elements) { 13609 // THIS CODE IS VERY HOT. Don't make changes without benchmarking. 13610 13611 if (elements) { 13612 13613 // if a Node (the most common case) 13614 if (elements.nodeType) { 13615 root[root.length++] = elements; 13616 } else { 13617 var length = elements.length; 13618 13619 // if an Array or NodeList and not a Window 13620 if (typeof length === 'number' && elements.window !== elements) { 13621 if (length) { 13622 for (var i = 0; i < length; i++) { 13623 root[root.length++] = elements[i]; 13624 } 13625 } 13626 } else { 13627 root[root.length++] = elements; 13628 } 13629 } 13630 } 13631} 13632 13633 13634function jqLiteController(element, name) { 13635 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); 13636} 13637 13638function jqLiteInheritedData(element, name, value) { 13639 // if element is the document object work with the html element instead 13640 // this makes $(document).scope() possible 13641 if (element.nodeType === NODE_TYPE_DOCUMENT) { 13642 element = element.documentElement; 13643 } 13644 var names = isArray(name) ? name : [name]; 13645 13646 while (element) { 13647 for (var i = 0, ii = names.length; i < ii; i++) { 13648 if (isDefined(value = jqLite.data(element, names[i]))) return value; 13649 } 13650 13651 // If dealing with a document fragment node with a host element, and no parent, use the host 13652 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM 13653 // to lookup parent controllers. 13654 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host); 13655 } 13656} 13657 13658function jqLiteEmpty(element) { 13659 jqLiteDealoc(element, true); 13660 while (element.firstChild) { 13661 element.removeChild(element.firstChild); 13662 } 13663} 13664 13665function jqLiteRemove(element, keepData) { 13666 if (!keepData) jqLiteDealoc(element); 13667 var parent = element.parentNode; 13668 if (parent) parent.removeChild(element); 13669} 13670 13671 13672function jqLiteDocumentLoaded(action, win) { 13673 win = win || window; 13674 if (win.document.readyState === 'complete') { 13675 // Force the action to be run async for consistent behavior 13676 // from the action's point of view 13677 // i.e. it will definitely not be in a $apply 13678 win.setTimeout(action); 13679 } else { 13680 // No need to unbind this handler as load is only ever called once 13681 jqLite(win).on('load', action); 13682 } 13683} 13684 13685function jqLiteReady(fn) { 13686 function trigger() { 13687 window.document.removeEventListener('DOMContentLoaded', trigger); 13688 window.removeEventListener('load', trigger); 13689 fn(); 13690 } 13691 13692 // check if document is already loaded 13693 if (window.document.readyState === 'complete') { 13694 window.setTimeout(fn); 13695 } else { 13696 // We can not use jqLite since we are not done loading and jQuery could be loaded later. 13697 13698 // Works for modern browsers and IE9 13699 window.document.addEventListener('DOMContentLoaded', trigger); 13700 13701 // Fallback to window.onload for others 13702 window.addEventListener('load', trigger); 13703 } 13704} 13705 13706////////////////////////////////////////// 13707// Functions which are declared directly. 13708////////////////////////////////////////// 13709var JQLitePrototype = JQLite.prototype = { 13710 ready: jqLiteReady, 13711 toString: function() { 13712 var value = []; 13713 forEach(this, function(e) { value.push('' + e);}); 13714 return '[' + value.join(', ') + ']'; 13715 }, 13716 13717 eq: function(index) { 13718 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); 13719 }, 13720 13721 length: 0, 13722 push: push, 13723 sort: [].sort, 13724 splice: [].splice 13725}; 13726 13727////////////////////////////////////////// 13728// Functions iterating getter/setters. 13729// these functions return self on setter and 13730// value on get. 13731////////////////////////////////////////// 13732var BOOLEAN_ATTR = {}; 13733forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { 13734 BOOLEAN_ATTR[lowercase(value)] = value; 13735}); 13736var BOOLEAN_ELEMENTS = {}; 13737forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { 13738 BOOLEAN_ELEMENTS[value] = true; 13739}); 13740var ALIASED_ATTR = { 13741 'ngMinlength': 'minlength', 13742 'ngMaxlength': 'maxlength', 13743 'ngMin': 'min', 13744 'ngMax': 'max', 13745 'ngPattern': 'pattern', 13746 'ngStep': 'step' 13747}; 13748 13749function getBooleanAttrName(element, name) { 13750 // check dom last since we will most likely fail on name 13751 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; 13752 13753 // booleanAttr is here twice to minimize DOM access 13754 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; 13755} 13756 13757function getAliasedAttrName(name) { 13758 return ALIASED_ATTR[name]; 13759} 13760 13761forEach({ 13762 data: jqLiteData, 13763 removeData: jqLiteRemoveData, 13764 hasData: jqLiteHasData, 13765 cleanData: jqLiteCleanData
vendor: 15,519 bytes, lines 13765-14306
13765 13766}, function(fn, name) { 13767 JQLite[name] = fn; 13768}); 13769 13770forEach({ 13771 data: jqLiteData, 13772 inheritedData: jqLiteInheritedData, 13773 13774 scope: function(element) { 13775 // Can't use jqLiteData here directly so we stay compatible with jQuery! 13776 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); 13777 }, 13778 13779 isolateScope: function(element) { 13780 // Can't use jqLiteData here directly so we stay compatible with jQuery! 13781 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); 13782 }, 13783 13784 controller: jqLiteController, 13785 13786 injector: function(element) { 13787 return jqLiteInheritedData(element, '$injector'); 13788 }, 13789 13790 removeAttr: function(element, name) { 13791 element.removeAttribute(name); 13792 }, 13793 13794 hasClass: jqLiteHasClass, 13795 13796 css: function(element, name, value) { 13797 name = cssKebabToCamel(name); 13798 13799 if (isDefined(value)) { 13800 element.style[name] = value; 13801 } else { 13802 return element.style[name]; 13803 } 13804 }, 13805 13806 attr: function(element, name, value) { 13807 var ret; 13808 var nodeType = element.nodeType; 13809 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT || 13810 !element.getAttribute) { 13811 return; 13812 } 13813 13814 var lowercasedName = lowercase(name); 13815 var isBooleanAttr = BOOLEAN_ATTR[lowercasedName]; 13816 13817 if (isDefined(value)) { 13818 // setter 13819 13820 if (value === null || (value === false && isBooleanAttr)) { 13821 element.removeAttribute(name); 13822 } else { 13823 element.setAttribute(name, isBooleanAttr ? lowercasedName : value); 13824 } 13825 } else { 13826 // getter 13827 13828 ret = element.getAttribute(name); 13829 13830 if (isBooleanAttr && ret !== null) { 13831 ret = lowercasedName; 13832 } 13833 // Normalize non-existing attributes to undefined (as jQuery). 13834 return ret === null ? undefined : ret; 13835 } 13836 }, 13837 13838 prop: function(element, name, value) { 13839 if (isDefined(value)) { 13840 element[name] = value; 13841 } else { 13842 return element[name]; 13843 } 13844 }, 13845 13846 text: (function() { 13847 getText.$dv = ''; 13848 return getText; 13849 13850 function getText(element, value) { 13851 if (isUndefined(value)) { 13852 var nodeType = element.nodeType; 13853 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : ''; 13854 } 13855 element.textContent = value; 13856 } 13857 })(), 13858 13859 val: function(element, value) { 13860 if (isUndefined(value)) { 13861 if (element.multiple && nodeName_(element) === 'select') { 13862 var result = []; 13863 forEach(element.options, function(option) { 13864 if (option.selected) { 13865 result.push(option.value || option.text); 13866 } 13867 }); 13868 return result; 13869 } 13870 return element.value; 13871 } 13872 element.value = value; 13873 }, 13874 13875 html: function(element, value) { 13876 if (isUndefined(value)) { 13877 return element.innerHTML; 13878 } 13879 jqLiteDealoc(element, true); 13880 element.innerHTML = value; 13881 }, 13882 13883 empty: jqLiteEmpty 13884}, function(fn, name) { 13885 /** 13886 * Properties: writes return selection, reads return first value 13887 */ 13888 JQLite.prototype[name] = function(arg1, arg2) { 13889 var i, key; 13890 var nodeCount = this.length; 13891 13892 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it 13893 // in a way that survives minification. 13894 // jqLiteEmpty takes no arguments but is a setter. 13895 if (fn !== jqLiteEmpty && 13896 (isUndefined((fn.length === 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) { 13897 if (isObject(arg1)) { 13898 13899 // we are a write, but the object properties are the key/values 13900 for (i = 0; i < nodeCount; i++) { 13901 if (fn === jqLiteData) { 13902 // data() takes the whole object in jQuery 13903 fn(this[i], arg1); 13904 } else { 13905 for (key in arg1) { 13906 fn(this[i], key, arg1[key]); 13907 } 13908 } 13909 } 13910 // return self for chaining 13911 return this; 13912 } else { 13913 // we are a read, so read the first child. 13914 // TODO: do we still need this? 13915 var value = fn.$dv; 13916 // Only if we have $dv do we iterate over all, otherwise it is just the first element. 13917 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount; 13918 for (var j = 0; j < jj; j++) { 13919 var nodeValue = fn(this[j], arg1, arg2); 13920 value = value ? value + nodeValue : nodeValue; 13921 } 13922 return value; 13923 } 13924 } else { 13925 // we are a write, so apply to all children 13926 for (i = 0; i < nodeCount; i++) { 13927 fn(this[i], arg1, arg2); 13928 } 13929 // return self for chaining 13930 return this; 13931 } 13932 }; 13933}); 13934 13935function createEventHandler(element, events) { 13936 var eventHandler = function(event, type) { 13937 // jQuery specific api 13938 event.isDefaultPrevented = function() { 13939 return event.defaultPrevented; 13940 }; 13941 13942 var eventFns = events[type || event.type]; 13943 var eventFnsLength = eventFns ? eventFns.length : 0; 13944 13945 if (!eventFnsLength) return; 13946 13947 if (isUndefined(event.immediatePropagationStopped)) { 13948 var originalStopImmediatePropagation = event.stopImmediatePropagation; 13949 event.stopImmediatePropagation = function() { 13950 event.immediatePropagationStopped = true; 13951 13952 if (event.stopPropagation) { 13953 event.stopPropagation(); 13954 } 13955 13956 if (originalStopImmediatePropagation) { 13957 originalStopImmediatePropagation.call(event); 13958 } 13959 }; 13960 } 13961 13962 event.isImmediatePropagationStopped = function() { 13963 return event.immediatePropagationStopped === true; 13964 }; 13965 13966 // Some events have special handlers that wrap the real handler 13967 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper; 13968 13969 // Copy event handlers in case event handlers array is modified during execution. 13970 if ((eventFnsLength > 1)) { 13971 eventFns = shallowCopy(eventFns); 13972 } 13973 13974 for (var i = 0; i < eventFnsLength; i++) { 13975 if (!event.isImmediatePropagationStopped()) { 13976 handlerWrapper(element, event, eventFns[i]); 13977 } 13978 } 13979 }; 13980 13981 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all 13982 // events on `element` 13983 eventHandler.elem = element; 13984 return eventHandler; 13985} 13986 13987function defaultHandlerWrapper(element, event, handler) { 13988 handler.call(element, event); 13989} 13990 13991function specialMouseHandlerWrapper(target, event, handler) { 13992 // Refer to jQuery's implementation of mouseenter & mouseleave 13993 // Read about mouseenter and mouseleave: 13994 // http://www.quirksmode.org/js/events_mouse.html#link8 13995 var related = event.relatedTarget; 13996 // For mousenter/leave call the handler if related is outside the target. 13997 // NB: No relatedTarget if the mouse left/entered the browser window 13998 if (!related || (related !== target && !jqLiteContains.call(target, related))) { 13999 handler.call(target, event); 14000 } 14001} 14002 14003////////////////////////////////////////// 14004// Functions iterating traversal. 14005// These functions chain results into a single 14006// selector. 14007////////////////////////////////////////// 14008forEach({ 14009 removeData: jqLiteRemoveData, 14010 14011 on: function jqLiteOn(element, type, fn, unsupported) { 14012 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); 14013 14014 // Do not add event handlers to non-elements because they will not be cleaned up. 14015 if (!jqLiteAcceptsData(element)) { 14016 return; 14017 } 14018 14019 var expandoStore = jqLiteExpandoStore(element, true); 14020 var events = expandoStore.events; 14021 var handle = expandoStore.handle; 14022 14023 if (!handle) { 14024 handle = expandoStore.handle = createEventHandler(element, events); 14025 } 14026 14027 // http://jsperf.com/string-indexof-vs-split 14028 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; 14029 var i = types.length; 14030 14031 var addHandler = function(type, specialHandlerWrapper, noEventListener) { 14032 var eventFns = events[type]; 14033 14034 if (!eventFns) { 14035 eventFns = events[type] = []; 14036 eventFns.specialHandlerWrapper = specialHandlerWrapper; 14037 if (type !== '$destroy' && !noEventListener) { 14038 element.addEventListener(type, handle); 14039 } 14040 } 14041 14042 eventFns.push(fn); 14043 }; 14044 14045 while (i--) { 14046 type = types[i]; 14047 if (MOUSE_EVENT_MAP[type]) { 14048 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper); 14049 addHandler(type, undefined, true); 14050 } else { 14051 addHandler(type); 14052 } 14053 } 14054 }, 14055 14056 off: jqLiteOff, 14057 14058 one: function(element, type, fn) { 14059 element = jqLite(element); 14060 14061 //add the listener twice so that when it is called 14062 //you can remove the original function and still be 14063 //able to call element.off(ev, fn) normally 14064 element.on(type, function onFn() { 14065 element.off(type, fn); 14066 element.off(type, onFn); 14067 }); 14068 element.on(type, fn); 14069 }, 14070 14071 replaceWith: function(element, replaceNode) { 14072 var index, parent = element.parentNode; 14073 jqLiteDealoc(element); 14074 forEach(new JQLite(replaceNode), function(node) { 14075 if (index) { 14076 parent.insertBefore(node, index.nextSibling); 14077 } else { 14078 parent.replaceChild(node, element); 14079 } 14080 index = node; 14081 }); 14082 }, 14083 14084 children: function(element) { 14085 var children = []; 14086 forEach(element.childNodes, function(element) { 14087 if (element.nodeType === NODE_TYPE_ELEMENT) { 14088 children.push(element); 14089 } 14090 }); 14091 return children; 14092 }, 14093 14094 contents: function(element) { 14095 return element.contentDocument || element.childNodes || []; 14096 }, 14097 14098 append: function(element, node) { 14099 var nodeType = element.nodeType; 14100 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return; 14101 14102 node = new JQLite(node); 14103 14104 for (var i = 0, ii = node.length; i < ii; i++) { 14105 var child = node[i]; 14106 element.appendChild(child); 14107 } 14108 }, 14109 14110 prepend: function(element, node) { 14111 if (element.nodeType === NODE_TYPE_ELEMENT) { 14112 var index = element.firstChild; 14113 forEach(new JQLite(node), function(child) { 14114 element.insertBefore(child, index); 14115 }); 14116 } 14117 }, 14118 14119 wrap: function(element, wrapNode) { 14120 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]); 14121 }, 14122 14123 remove: jqLiteRemove, 14124 14125 detach: function(element) { 14126 jqLiteRemove(element, true); 14127 }, 14128 14129 after: function(element, newElement) { 14130 var index = element, parent = element.parentNode; 14131 14132 if (parent) { 14133 newElement = new JQLite(newElement); 14134 14135 for (var i = 0, ii = newElement.length; i < ii; i++) { 14136 var node = newElement[i]; 14137 parent.insertBefore(node, index.nextSibling); 14138 index = node; 14139 } 14140 } 14141 }, 14142 14143 addClass: jqLiteAddClass, 14144 removeClass: jqLiteRemoveClass, 14145 14146 toggleClass: function(element, selector, condition) { 14147 if (selector) { 14148 forEach(selector.split(' '), function(className) { 14149 var classCondition = condition; 14150 if (isUndefined(classCondition)) { 14151 classCondition = !jqLiteHasClass(element, className); 14152 } 14153 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); 14154 }); 14155 } 14156 }, 14157 14158 parent: function(element) { 14159 var parent = element.parentNode; 14160 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null; 14161 }, 14162 14163 next: function(element) { 14164 return element.nextElementSibling; 14165 }, 14166 14167 find: function(element, selector) { 14168 if (element.getElementsByTagName) { 14169 return element.getElementsByTagName(selector); 14170 } else { 14171 return []; 14172 } 14173 }, 14174 14175 clone: jqLiteClone, 14176 14177 triggerHandler: function(element, event, extraParameters) { 14178 14179 var dummyEvent, eventFnsCopy, handlerArgs; 14180 var eventName = event.type || event; 14181 var expandoStore = jqLiteExpandoStore(element); 14182 var events = expandoStore && expandoStore.events; 14183 var eventFns = events && events[eventName]; 14184 14185 if (eventFns) { 14186 // Create a dummy event to pass to the handlers 14187 dummyEvent = { 14188 preventDefault: function() { this.defaultPrevented = true; }, 14189 isDefaultPrevented: function() { return this.defaultPrevented === true; }, 14190 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; }, 14191 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; }, 14192 stopPropagation: noop, 14193 type: eventName, 14194 target: element 14195 }; 14196 14197 // If a custom event was provided then extend our dummy event with it 14198 if (event.type) { 14199 dummyEvent = extend(dummyEvent, event); 14200 } 14201 14202 // Copy event handlers in case event handlers array is modified during execution. 14203 eventFnsCopy = shallowCopy(eventFns); 14204 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; 14205 14206 forEach(eventFnsCopy, function(fn) { 14207 if (!dummyEvent.isImmediatePropagationStopped()) { 14208 fn.apply(element, handlerArgs); 14209 } 14210 }); 14211 } 14212 } 14213}, function(fn, name) { 14214 /** 14215 * chaining functions 14216 */ 14217 JQLite.prototype[name] = function(arg1, arg2, arg3) { 14218 var value; 14219 14220 for (var i = 0, ii = this.length; i < ii; i++) { 14221 if (isUndefined(value)) { 14222 value = fn(this[i], arg1, arg2, arg3); 14223 if (isDefined(value)) { 14224 // any function which returns a value needs to be wrapped 14225 value = jqLite(value); 14226 } 14227 } else { 14228 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); 14229 } 14230 } 14231 return isDefined(value) ? value : this; 14232 }; 14233}); 14234 14235// bind legacy bind/unbind to on/off 14236JQLite.prototype.bind = JQLite.prototype.on; 14237JQLite.prototype.unbind = JQLite.prototype.off; 14238 14239 14240// Provider for private $$jqLite service 14241/** @this */ 14242function $$jqLiteProvider() { 14243 this.$get = function $$jqLite() { 14244 return extend(JQLite, { 14245 hasClass: function(node, classes) { 14246 if (node.attr) node = node[0]; 14247 return jqLiteHasClass(node, classes); 14248 }, 14249 addClass: function(node, classes) { 14250 if (node.attr) node = node[0]; 14251 return jqLiteAddClass(node, classes); 14252 }, 14253 removeClass: function(node, classes) { 14254 if (node.attr) node = node[0]; 14255 return jqLiteRemoveClass(node, classes); 14256 } 14257 }); 14258 }; 14259} 14260 14261/** 14262 * Computes a hash of an 'obj'. 14263 * Hash of a: 14264 * string is string 14265 * number is number as string 14266 * object is either result of calling $$hashKey function on the object or uniquely generated id, 14267 * that is also assigned to the $$hashKey property of the object. 14268 * 14269 * @param obj 14270 * @returns {string} hash string such that the same input will have the same hash string. 14271 * The resulting string key is in 'type:hashKey' format. 14272 */ 14273function hashKey(obj, nextUidFn) { 14274 var key = obj && obj.$$hashKey; 14275 14276 if (key) { 14277 if (typeof key === 'function') { 14278 key = obj.$$hashKey(); 14279 } 14280 return key; 14281 } 14282 14283 var objType = typeof obj; 14284 if (objType === 'function' || (objType === 'object' && obj !== null)) { 14285 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)(); 14286 } else { 14287 key = objType + ':' + obj; 14288 } 14289 14290 return key; 14291} 14292 14293// A minimal ES2015 Map implementation. 14294// Should be bug/feature equivalent to the native implementations of supported browsers 14295// (for the features required in Angular). 14296// See https://kangax.github.io/compat-table/es6/#test-Map 14297var nanKey = Object.create(null); 14298function NgMapShim() { 14299 this._keys = []; 14300 this._values = []; 14301 this._lastKey = NaN; 14302 this._lastIndex = -1; 14303} 14304NgMapShim.prototype = { 14305 _idx: function(key) { 14306 if (key
vendor: 1,720 bytes, lines 14306-14370
14306=== this._lastKey) { 14307 return this._lastIndex; 14308 } 14309 this._lastKey = key; 14310 this._lastIndex = this._keys.indexOf(key); 14311 return this._lastIndex; 14312 }, 14313 _transformKey: function(key) { 14314 return isNumberNaN(key) ? nanKey : key; 14315 }, 14316 get: function(key) { 14317 key = this._transformKey(key); 14318 var idx = this._idx(key); 14319 if (idx !== -1) { 14320 return this._values[idx]; 14321 } 14322 }, 14323 set: function(key, value) { 14324 key = this._transformKey(key); 14325 var idx = this._idx(key); 14326 if (idx === -1) { 14327 idx = this._lastIndex = this._keys.length; 14328 } 14329 this._keys[idx] = key; 14330 this._values[idx] = value; 14331 14332 // Support: IE11 14333 // Do not `return this` to simulate the partial IE11 implementation 14334 }, 14335 delete: function(key) { 14336 key = this._transformKey(key); 14337 var idx = this._idx(key); 14338 if (idx === -1) { 14339 return false; 14340 } 14341 this._keys.splice(idx, 1); 14342 this._values.splice(idx, 1); 14343 this._lastKey = NaN; 14344 this._lastIndex = -1; 14345 return true; 14346 } 14347}; 14348 14349// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations 14350// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map` 14351// implementations get more stable, we can reconsider switching to `window.Map` (when available). 14352var NgMap = NgMapShim; 14353 14354var $$MapProvider = [/** @this */function() { 14355 this.$get = [function() { 14356 return NgMap; 14357 }]; 14358}]; 14359 14360/** 14361 * @ngdoc function 14362 * @module ng 14363 * @name angular.injector 14364 * @kind function 14365 * 14366 * @description 14367 * Creates an injector object that can be used for retrieving services as well as for 14368 * dependency injection (see {@link guide/di dependency injection}). 14369 * 14370 * @param {Array.<string|Function>}
14370 modules A list of module functions or their aliases. See 14371 * {@link angular.module}. The `ng` module must be explicitly added. 14372 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which 14373 * disallows argument name annotation inference. 14374 * @returns {injector} Injector object. See {@link auto.$injector $injector}. 14375 * 14376 * @example 14377 * Typical usage 14378 * ```js 14379 * // create an injector 14380 * var $injector = angular.injector(['ng']); 14381 * 14382 * // use the injector to kick off your application 14383 * // use the type inference to auto inject arguments, or use implicit injection 14384 * $injector.invoke(function($rootScope, $compile, $document) { 14385 * $compile($document)($rootScope); 14386 * $rootScope.$digest(); 14387 * }); 14388 * ``` 14389 * 14390 * Sometimes you want to get access to the injector of a currently running Angular app 14391 * from outside Angular. Perhaps, you want to inject and compile some markup after the 14392 * application has been bootstrapped. You can do this using the extra `injector()` added 14393 * to JQuery/jqLite elements. See {@link angular.element}. 14394 * 14395 * *This is fairly rare but could be the case if a third party library is injecting the 14396 * markup.* 14397 * 14398 * In the following example a new block of HTML containing a `ng-controller` 14399 * directive is added to the end of the document body by JQuery. We then compile and link 14400 * it into the current AngularJS scope. 14401 * 14402 * ```js 14403 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>'); 14404 * $(document.body).append($div); 14405 * 14406 * angular.element(document).injector().invoke(function($compile) { 14407 * var scope = angular.element($div).scope(); 14408 * $compile($div)(scope); 14409 * }); 14410 * ``` 14411 */ 14412 14413 14414/** 14415 * @ngdoc module 14416 * @name auto 14417 * @installation 14418 * @description 14419 * 14420 * Implicit module which gets automatically added to each {@link auto.$injector $injector}. 14421 */ 14422 14423var ARROW_ARG = /^([^(]+?)=>/; 14424var FN_ARGS = /^[^(]*\(\s*([^)]*)\)/m; 14425var FN_ARG_SPLIT = /,/; 14426var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; 14427var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 14428var $injectorMinErr = minErr('$injector'); 14429 14430function stringifyFn(fn) { 14431 return Function.prototype.toString.call(fn); 14432} 14433 14434function extractArgs(fn) { 14435 var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''), 14436 args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS); 14437 return args; 14438} 14439 14440function anonFn(fn) { 14441 // For anonymous functions, showing at the very least the function signature can help in 14442 // debugging. 14443 var args = extractArgs(fn); 14444 if (args) { 14445 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; 14446 } 14447 return 'fn'; 14448} 14449 14450function annotate(fn, strictDi, name) { 14451 var $inject, 14452 argDecl, 14453 last; 14454 14455 if (typeof fn === 'function') { 14456 if (!($inject = fn.$inject)) { 14457 $inject = []; 14458 if (fn.length) { 14459 if (strictDi) { 14460 if (!isString(name) || !name) { 14461 name = fn.name || anonFn(fn); 14462 } 14463 throw $injectorMinErr('strictdi', 14464 '{0} is not using explicit annotation and cannot be invoked in strict mode', name); 14465 } 14466 argDecl = extractArgs(fn); 14467 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) { 14468 arg.replace(FN_ARG, function(all, underscore, name) { 14469 $inject.push(name); 14470 }); 14471 }); 14472 } 14473 fn.$inject = $inject; 14474 } 14475 } else if (isArray(fn)) { 14476 last = fn.length - 1; 14477 assertArgFn(fn[last], 'fn'); 14478 $inject = fn.slice(0, last); 14479 } else { 14480 assertArgFn(fn, 'fn', true); 14481 } 14482 return $inject; 14483} 14484 14485/////////////////////////////////////// 14486 14487/** 14488 * @ngdoc service 14489 * @name $injector 14490 * 14491 * @description 14492 * 14493 * `$injector` is used to retrieve object instances as defined by 14494 * {@link auto.$provide provider}, instantiate types, invoke methods, 14495 * and load modules. 14496 * 14497 * The following always holds true: 14498 * 14499 * ```js 14500 * var $injector = angular.injector(); 14501 * expect($injector.get('$injector')).toBe($injector); 14502 * expect($injector.invoke(function($injector) { 14503 * return $injector; 14504 * })).toBe($injector); 14505 * ``` 14506 * 14507 * # Injection Function Annotation 14508 * 14509 * JavaScript does not have annotations, and annotations are needed for dependency injection. The 14510 * following are all valid ways of annotating function with injection arguments and are equivalent. 14511 * 14512 * ```js 14513 * // inferred (only works if code not minified/obfuscated) 14514 * $injector.invoke(function(serviceA){}); 14515 * 14516 * // annotated 14517 * function explicit(serviceA) {}; 14518 * explicit.$inject = ['serviceA']; 14519 * $injector.invoke(explicit); 14520 * 14521 * // inline 14522 * $injector.invoke(['serviceA', function(serviceA){}]); 14523 * ``` 14524 * 14525 * ## Inference 14526 * 14527 * In JavaScript calling `toString()` on a function returns the function definition. The definition 14528 * can then be parsed and the function arguments can be extracted. This method of discovering 14529 * annotations is disallowed when the injector is in strict mode. 14530 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the 14531 * argument names. 14532 * 14533 * ## `$inject` Annotation 14534 * By adding an `$inject` property onto a function the injection parameters can be specified. 14535 * 14536 * ## Inline 14537 * As an array of injection names, where the last item in the array is the function to call. 14538 */ 14539 14540/** 14541 * @ngdoc property 14542 * @name $injector#modules 14543 * @type {Object} 14544 * @description 14545 * A hash containing all the modules that have been loaded into the 14546 * $injector. 14547 * 14548 * You can use this property to find out information about a module via the 14549 * {@link angular.Module#info `myModule.info(...)`} method. 14550 * 14551 * For example: 14552 * 14553 * ``` 14554 * var info = $injector.modules['ngAnimate'].info(); 14555 * ``` 14556 * 14557 * **Do not use this property to attempt to modify the modules after the application 14558 * has been bootstrapped.** 14559 */ 14560 14561 14562/** 14563 * @ngdoc method 14564 * @name $injector#get 14565 * 14566 * @description 14567 * Return an instance of the service. 14568 * 14569 * @param {string} name The name of the instance to retrieve. 14570 * @param {string=} caller An optional string to provide the origin of the function call for error messages. 14571 * @return {*} The instance. 14572 */ 14573 14574/** 14575 * @ngdoc method 14576 * @name $injector#invoke 14577 * 14578 * @description 14579 * Invoke the method and supply the method arguments from the `$injector`. 14580 * 14581 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are 14582 * injected according to the {@link guide/di $inject Annotation} rules. 14583 * @param {Object=} self The `this` for the invoked method. 14584 * @param {Object=} locals Optional object. If preset then any argument names are read from this 14585 * object first, before the `$injector` is consulted. 14586 * @returns {*} the value returned by the invoked `fn` function. 14587 */ 14588 14589/** 14590 * @ngdoc method 14591 * @name $injector#has 14592 * 14593 * @description 14594 * Allows the user to query if the particular service exists. 14595 * 14596 * @param {string} name Name of the service to query. 14597 * @returns {boolean} `true` if injector has given service. 14598 */ 14599 14600/** 14601 * @ngdoc method 14602 * @name $injector#instantiate 14603 * @description 14604 * Create a new instance of JS type. The method takes a constructor function, invokes the new 14605 * operator, and supplies all of the arguments to the constructor function as specified by the 14606 * constructor annotation. 14607 * 14608 * @param {Function} Type Annotated constructor function. 14609 * @param {Object=} locals Optional object. If preset then any argument names are read from this 14610 * object first, before the `$injector` is consulted. 14611 * @returns {Object} new instance of `Type`. 14612 */ 14613 14614/** 14615 * @ngdoc method 14616 * @name $injector#annotate 14617 * 14618 * @description 14619 * Returns an array of service names which the function is requesting for injection. This API is 14620 * used by the injector to determine which services need to be injected into the function when the 14621 * function is invoked. There are three ways in which the function can be annotated with the needed 14622 * dependencies. 14623 * 14624 * # Argument names 14625 * 14626 * The simplest form is to extract the dependencies from the arguments of the function. This is done
14627 * by converting the function into a string using `toString()` method and extracting the argument 14628 * names. 14629 * ```js 14630 * // Given 14631 * function MyController($scope, $route) { 14632 * // ... 14633 * } 14634 * 14635 * // Then 14636 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); 14637 * ``` 14638 * 14639 * You can disallow this method by using strict injection mode. 14640 * 14641 * This method does not work with code minification / obfuscation. For this reason the following 14642 * annotation strategies are supported. 14643 * 14644 * # The `$inject` property 14645 * 14646 * If a function has an `$inject` property and its value is an array of strings, then the strings 14647 * represent names of services to be injected into the function. 14648 * ```js 14649 * // Given 14650 * var MyController = function(obfuscatedScope, obfuscatedRoute) { 14651 * // ... 14652 * } 14653 * // Define function dependencies 14654 * MyController['$inject'] = ['$scope', '$route']; 14655 * 14656 * // Then 14657 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); 14658 * ``` 14659 * 14660 * # The array notation 14661 * 14662 * It is often desirable to inline Injected functions and that's when setting the `$inject` property 14663 * is very inconvenient. In these situations using the array notation to specify the dependencies in 14664 * a way that survives minification is a better choice: 14665 * 14666 * ```js 14667 * // We wish to write this (not minification / obfuscation safe) 14668 * injector.invoke(function($compile, $rootScope) { 14669 * // ... 14670 * }); 14671 * 14672 * // We are forced to write break inlining 14673 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { 14674 * // ... 14675 * }; 14676 * tmpFn.$inject = ['$compile', '$rootScope']; 14677 * injector.invoke(tmpFn); 14678 * 14679 * // To better support inline function the inline annotation is supported 14680 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { 14681 * // ... 14682 * }]); 14683 * 14684 * // Therefore 14685 * expect(injector.annotate( 14686 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) 14687 * ).toEqual(['$compile', '$rootScope']); 14688 * ``` 14689 * 14690 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to 14691 * be retrieved as described above. 14692 * 14693 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. 14694 * 14695 * @returns {Array.<string>} The names of the services which the function requires. 14696 */ 14697 14698 14699 14700/** 14701 * @ngdoc service 14702 * @name $provide 14703 * 14704 * @description 14705 * 14706 * The {@link auto.$provide $provide} service has a number of methods for registering components 14707 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on 14708 * {@link angular.Module}. 14709 * 14710 * An Angular **service** is a singleton object created by a **service factory**. These **service 14711 * factories** are functions which, in turn, are created by a **service provider**. 14712 * The **service providers** are constructor functions. When instantiated they must contain a 14713 * property called `$get`, which holds the **service factory** function. 14714 * 14715 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the 14716 * correct **service provider**, instantiating it and then calling its `$get` **service factory** 14717 * function to get the instance of the **service**. 14718 * 14719 * Often services have no configuration options and there is no need to add methods to the service 14720 * provider. The provider will be no more than a constructor function with a `$get` property. For 14721 * these cases the {@link auto.$provide $provide} service has additional helper methods to register 14722 * services without specifying a provider. 14723 * 14724 * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the 14725 * {@link auto.$injector $injector} 14726 * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by 14727 * providers and services. 14728 * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by 14729 * services, not providers. 14730 * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function** 14731 * that will be wrapped in a **service provider** object, whose `$get` property will contain the 14732 * given factory function. 14733 * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function** 14734 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate 14735 * a new object using the given constructor function. 14736 * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that 14737 * will be able to modify or replace the implementation of another service. 14738 * 14739 * See the individual methods for more information and examples. 14740 */ 14741 14742/** 14743 * @ngdoc method 14744 * @name $provide#provider 14745 * @description 14746 * 14747 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions 14748 * are constructor functions, whose instances are responsible for "providing" a factory for a 14749 * service. 14750 * 14751 * Service provider names start with the name of the service they provide followed by `Provider`. 14752 * For example, the {@link ng.$log $log} service has a provider called 14753 * {@link ng.$logProvider $logProvider}. 14754 * 14755 * Service provider objects can have additional methods which allow configuration of the provider 14756 * and its service. Importantly, you can configure what kind of service is created by the `$get` 14757 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a 14758 * method {@link ng.$logProvider#debugEnabled debugEnabled} 14759 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the 14760 * console or not. 14761 * 14762 * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 14763 'Provider'` key. 14764 * @param {(Object|function())} provider If the provider is: 14765 * 14766 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using 14767 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. 14768 * - `Constructor`: a new instance of the provider will be created using 14769 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. 14770 * 14771 * @returns {Object} registered provider instance 14772 14773 * @example 14774 * 14775 * The following example shows how to create a simple event tracking service and register it using 14776 * {@link auto.$provide#provider $provide.provider()}. 14777 * 14778 * ```js 14779 * // Define the eventTracker provider 14780 * function EventTrackerProvider() { 14781 * var trackingUrl = '/track'; 14782 * 14783 * // A provider method for configuring where the tracked events should been saved 14784 * this.setTrackingUrl = function(url) { 14785 * trackingUrl = url; 14786 * }; 14787 * 14788 * // The service factory function 14789 * this.$get = ['$http', function($http) { 14790 * var trackedEvents = {}; 14791 * return { 14792 * // Call this to track an event 14793 * event: function(event) { 14794 * var count = trackedEvents[event] || 0; 14795 * count += 1; 14796 * trackedEvents[event] = count; 14797 * return count; 14798 * }, 14799 * // Call this to save the tracked events to the trackingUrl 14800 * save: function() { 14801 * $http.post(trackingUrl, trackedEvents); 14802 * } 14803 * }; 14804 * }]; 14805 * } 14806 * 14807 * describe('eventTracker', function() { 14808 * var postSpy; 14809 * 14810 * beforeEach(module(function($provide) { 14811 * // Register the eventTracker provider 14812 * $provide.provider('eventTracker', EventTrackerProvider); 14813 * })); 14814 * 14815 * beforeEach(module(function(eventTrackerProvider) { 14816 * // Configure eventTracker provider 14817 * eventTrackerProvider.setTrackingUrl('/custom-track'); 14818 * })); 14819 * 14820 * it('tracks events', inject(function(eventTracker) { 14821 * expect(eventTracker.event('login')).toEqual(1); 14822 * expect(eventTracker.event('login')).toEqual(2); 14823 * })); 14824 * 14825 * it('saves to the tracking url', inject(function(eventTracker, $http) { 14826 * postSpy = spyOn($http, 'post'); 14827 * eventTracker.event('login'); 14828 * eventTracker.save(); 14829 * expect(postSpy).toHaveBeenCalled(); 14830 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); 14831 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); 14832 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); 14833 * })); 14834 * }); 14835 * ``` 14836 */ 14837 14838/** 14839 * @ngdoc method 14840 * @name $provide#factory 14841 * @description 14842 * 14843 * Register a **service factory**, which will be called to return the service instance. 14844 * This is short for registering a service where its provider consists of only a `$get` property, 14845 * which is the given service factory function. 14846 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to 14847 * configure your service in a provider. 14848 * 14849 * @param {string} name The name of the instance. 14850 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation. 14851 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. 14852 * @returns {Object} registered provider instance 14853 * 14854 * @example 14855 * Here is an example of registering a service 14856 * ```js 14857 * $provide.factory('ping', ['$http', function($http) { 14858 * return function ping() { 14859 * return $http.send('/ping'); 14860 * }; 14861 * }]); 14862 * ``` 14863 * You would then inject and use this service like this: 14864 * ```js 14865 * someModule.controller('Ctrl', ['ping', function(ping) { 14866 * ping(); 14867 * }]); 14868 * ``` 14869 */ 14870 14871 14872/** 14873 * @ngdoc method 14874 * @name $provide#service 14875 * @description 14876 * 14877 * Register a **service constructor**, which will be invoked with `new` to create the service 14878 * instance. 14879 * This is short for registering a service where its provider's `$get` property is a factory 14880 * function that returns an instance instantiated by the injector from the service constructor 14881 * function. 14882 * 14883 * Internally it looks a bit like this: 14884 * 14885 * ``` 14886 * { 14887 * $get: function() { 14888 * return $injector.instantiate(constructor); 14889 * } 14890 * } 14891 * ``` 14892 * 14893 * 14894 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service 14895 * as a type/class. 14896 * 14897 * @param {string} name The name of the instance. 14898 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function) 14899 * that will be instantiated. 14900 * @returns {Object} registered provider instance 14901 * 14902 * @example 14903 * Here is an example of registering a service using 14904 * {@link auto.$provide#service $provide.service(class)}. 14905 * ```js 14906 * var Ping = function($http) { 14907 * this.$http = $http; 14908 * }; 14909 * 14910 * Ping.$inject = ['$http']; 14911 * 14912 * Ping.prototype.send = function() { 14913 * return this.$http.get('/ping'); 14914 * }; 14915 * $provide.service('ping', Ping); 14916 * ``` 14917 * You would then inject and use this service like this: 14918 * ```js 14919 * someModule.controller('Ctrl', ['ping', function(ping) { 14920 * ping.send(); 14921 * }]); 14922 * ``` 14923 */ 14924 14925 14926/** 14927 * @ngdoc method 14928 * @name $provide#value 14929 * @description 14930 * 14931 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a 14932 * number, an array, an object or a function. This is short for registering a service where its 14933 * provider's `$get` property is a factory function that takes no arguments and returns the **value 14934 * service**. That also means it is not possible to inject other services into a value service. 14935 * 14936 * Value services are similar to constant services, except that they cannot be injected into a 14937 * module configuration function (see {@link angular.Module#config}) but they can be overridden by 14938 * an Angular {@link auto.$provide#decorator decorator}. 14939 * 14940 * @param {string} name The name of the instance. 14941 * @param {*} value The value. 14942 * @returns {Object} registered provider instance 14943 * 14944 * @example 14945 * Here are some examples of creating value services. 14946 * ```js 14947 * $provide.value('ADMIN_USER', 'admin'); 14948 *
14949 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); 14950 * 14951 * $provide.value('halfOf', function(value) { 14952 * return value / 2; 14953 * }); 14954 * ``` 14955 */ 14956 14957 14958/** 14959 * @ngdoc method 14960 * @name $provide#constant 14961 * @description 14962 * 14963 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string, 14964 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not 14965 * possible to inject other services into a constant. 14966 * 14967 * But unlike {@link auto.$provide#value value}, a constant can be 14968 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot 14969 * be overridden by an Angular {@link auto.$provide#decorator decorator}. 14970 * 14971 * @param {string} name The name of the constant. 14972 * @param {*} value The constant value. 14973 * @returns {Object} registered instance 14974 * 14975 * @example 14976 * Here a some examples of creating constants: 14977 * ```js 14978 * $provide.constant('SHARD_HEIGHT', 306); 14979 * 14980 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); 14981 * 14982 * $provide.constant('double', function(value) { 14983 * return value * 2; 14984 * }); 14985 * ``` 14986 */ 14987 14988 14989/** 14990 * @ngdoc method 14991 * @name $provide#decorator 14992 * @description 14993 * 14994 * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function 14995 * intercepts the creation of a service, allowing it to override or modify the behavior of the 14996 * service. The return value of the decorator function may be the original service, or a new service 14997 * that replaces (or wraps and delegates to) the original service. 14998 * 14999 * You can find out more about using decorators in the {@link guide/decorators} guide. 15000 * 15001 * @param {string} name The name of the service to decorate. 15002 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be 15003 * provided and should return the decorated service instance. The function is called using 15004 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. 15005 * Local injection arguments: 15006 * 15007 * * `$delegate` - The original service instance, which can be replaced, monkey patched, configured, 15008 * decorated or delegated to. 15009 * 15010 * @example 15011 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting 15012 * calls to {@link ng.$log#error $log.warn()}. 15013 * ```js 15014 * $provide.decorator('$log', ['$delegate', function($delegate) { 15015 * $delegate.warn = $delegate.error; 15016 * return $delegate; 15017 * }]); 15018 * ``` 15019 */ 15020 15021 15022function createInjector(modulesToLoad, strictDi) { 15023 strictDi = (strictDi === true); 15024 var INSTANTIATING = {}, 15025 providerSuffix = 'Provider', 15026 path = [], 15027 loadedModules = new NgMap(), 15028 providerCache = { 15029 $provide: { 15030 provider: supportObject(provider), 15031 factory: supportObject(factory), 15032 service: supportObject(service), 15033 value: supportObject(value), 15034 constant: supportObject(constant), 15035 decorator: decorator 15036 } 15037 }, 15038 providerInjector = (providerCache.$injector = 15039 createInternalInjector(providerCache, function(serviceName, caller) { 15040 if (angular.isString(caller)) { 15041 path.push(caller); 15042 } 15043 throw $injectorMinErr('unpr', 'Unknown provider: {0}', path.join(' <- ')); 15044 })), 15045 instanceCache = {}, 15046 protoInstanceInjector = 15047 createInternalInjector(instanceCache, function(serviceName, caller) { 15048 var provider = providerInjector.get(serviceName + providerSuffix, caller); 15049 return instanceInjector.invoke( 15050 provider.$get, provider, undefined, serviceName); 15051 }), 15052 instanceInjector = protoInstanceInjector; 15053 15054 providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) }; 15055 instanceInjector.modules = providerInjector.modules = createMap(); 15056 var runBlocks = loadModules(modulesToLoad); 15057 instanceInjector = protoInstanceInjector.get('$injector'); 15058 instanceInjector.strictDi = strictDi; 15059 forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); }); 15060 15061 return instanceInjector; 15062 15063 //////////////////////////////////// 15064 // $provider 15065 //////////////////////////////////// 15066 15067 function supportObject(delegate) { 15068 return function(key, value) { 15069 if (isObject(key)) {
vendor: 5,894 bytes, lines 15070-15238
15070 forEach(key, reverseParams(delegate)); 15071 } else { 15072 return delegate(key, value); 15073 } 15074 }; 15075 } 15076 15077 function provider(name, provider_) { 15078 assertNotHasOwnProperty(name, 'service'); 15079 if (isFunction(provider_) || isArray(provider_)) { 15080 provider_ = providerInjector.instantiate(provider_); 15081 } 15082 if (!provider_.$get) { 15083 throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name); 15084 } 15085 return (providerCache[name + providerSuffix] = provider_); 15086 } 15087 15088 function enforceReturnValue(name, factory) { 15089 return /** @this */ function enforcedReturnValue() { 15090 var result = instanceInjector.invoke(factory, this); 15091 if (isUndefined(result)) { 15092 throw $injectorMinErr('undef', 'Provider \'{0}\' must return a value from $get factory method.', name); 15093 } 15094 return result; 15095 }; 15096 } 15097 15098 function factory(name, factoryFn, enforce) { 15099 return provider(name, { 15100 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn 15101 }); 15102 } 15103 15104 function service(name, constructor) { 15105 return factory(name, ['$injector', function($injector) { 15106 return $injector.instantiate(constructor); 15107 }]); 15108 } 15109 15110 function value(name, val) { return factory(name, valueFn(val), false); } 15111 15112 function constant(name, value) { 15113 assertNotHasOwnProperty(name, 'constant'); 15114 providerCache[name] = value; 15115 instanceCache[name] = value; 15116 } 15117 15118 function decorator(serviceName, decorFn) { 15119 var origProvider = providerInjector.get(serviceName + providerSuffix), 15120 orig$get = origProvider.$get; 15121 15122 origProvider.$get = function() { 15123 var origInstance = instanceInjector.invoke(orig$get, origProvider); 15124 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); 15125 }; 15126 } 15127 15128 //////////////////////////////////// 15129 // Module Loading 15130 //////////////////////////////////// 15131 function loadModules(modulesToLoad) { 15132 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); 15133 var runBlocks = [], moduleFn; 15134 forEach(modulesToLoad, function(module) { 15135 if (loadedModules.get(module)) return; 15136 loadedModules.set(module, true); 15137 15138 function runInvokeQueue(queue) { 15139 var i, ii; 15140 for (i = 0, ii = queue.length; i < ii; i++) { 15141 var invokeArgs = queue[i], 15142 provider = providerInjector.get(invokeArgs[0]); 15143 15144 provider[invokeArgs[1]].apply(provider, invokeArgs[2]); 15145 } 15146 } 15147 15148 try { 15149 if (isString(module)) { 15150 moduleFn = angularModule(module); 15151 instanceInjector.modules[module] = moduleFn; 15152 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); 15153 runInvokeQueue(moduleFn._invokeQueue); 15154 runInvokeQueue(moduleFn._configBlocks); 15155 } else if (isFunction(module)) { 15156 runBlocks.push(providerInjector.invoke(module)); 15157 } else if (isArray(module)) { 15158 runBlocks.push(providerInjector.invoke(module)); 15159 } else { 15160 assertArgFn(module, 'module'); 15161 } 15162 } catch (e) { 15163 if (isArray(module)) { 15164 module = module[module.length - 1]; 15165 } 15166 if (e.message && e.stack && e.stack.indexOf(e.message) === -1) { 15167 // Safari & FF's stack traces don't contain error.message content 15168 // unlike those of Chrome and IE 15169 // So if stack doesn't contain message, we create a new string that contains both. 15170 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. 15171 // eslint-disable-next-line no-ex-assign 15172 e = e.message + '\n' + e.stack; 15173 } 15174 throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}', 15175 module, e.stack || e.message || e); 15176 } 15177 }); 15178 return runBlocks; 15179 } 15180 15181 //////////////////////////////////// 15182 // internal Injector 15183 //////////////////////////////////// 15184 15185 function createInternalInjector(cache, factory) { 15186 15187 function getService(serviceName, caller) { 15188 if (cache.hasOwnProperty(serviceName)) { 15189 if (cache[serviceName] === INSTANTIATING) { 15190 throw $injectorMinErr('cdep', 'Circular dependency found: {0}', 15191 serviceName + ' <- ' + path.join(' <- ')); 15192 } 15193 return cache[serviceName]; 15194 } else { 15195 try { 15196 path.unshift(serviceName); 15197 cache[serviceName] = INSTANTIATING; 15198 cache[serviceName] = factory(serviceName, caller); 15199 return cache[serviceName]; 15200 } catch (err) { 15201 if (cache[serviceName] === INSTANTIATING) { 15202 delete cache[serviceName]; 15203 } 15204 throw err; 15205 } finally { 15206 path.shift(); 15207 } 15208 } 15209 } 15210 15211 15212 function injectionArgs(fn, locals, serviceName) { 15213 var args = [], 15214 $inject = createInjector.$$annotate(fn, strictDi, serviceName); 15215 15216 for (var i = 0, length = $inject.length; i < length; i++) { 15217 var key = $inject[i]; 15218 if (typeof key !== 'string') { 15219 throw $injectorMinErr('itkn', 15220 'Incorrect injection token! Expected service name as string, got {0}', key); 15221 } 15222 args.push(locals && locals.hasOwnProperty(key) ? locals[key] : 15223 getService(key, serviceName)); 15224 } 15225 return args; 15226 } 15227 15228 function isClass(func) { 15229 // Support: IE 9-11 only 15230 // IE 9-11 do not support classes and IE9 leaks with the code below. 15231 if (msie || typeof func !== 'function') { 15232 return false; 15233 } 15234 var result = func.$$ngIsClass; 15235 if (!isBoolean(result)) { 15236 // Support: Edge 12-13 only 15237 // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/ 15238 result = func.$$ngIsClass = /^(?:class\b|constructor\()
vendor: 16,448 bytes, lines 15238-15742
15238/.test(stringifyFn(func)); 15239 } 15240 return result; 15241 } 15242 15243 function invoke(fn, self, locals, serviceName) { 15244 if (typeof locals === 'string') { 15245 serviceName = locals; 15246 locals = null; 15247 } 15248 15249 var args = injectionArgs(fn, locals, serviceName); 15250 if (isArray(fn)) { 15251 fn = fn[fn.length - 1]; 15252 } 15253 15254 if (!isClass(fn)) { 15255 // http://jsperf.com/angularjs-invoke-apply-vs-switch 15256 // #5388 15257 return fn.apply(self, args); 15258 } else { 15259 args.unshift(null); 15260 return new (Function.prototype.bind.apply(fn, args))(); 15261 } 15262 } 15263 15264 15265 function instantiate(Type, locals, serviceName) { 15266 // Check if Type is annotated and use just the given function at n-1 as parameter 15267 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); 15268 var ctor = (isArray(Type) ? Type[Type.length - 1] : Type); 15269 var args = injectionArgs(Type, locals, serviceName); 15270 // Empty object at position 0 is ignored for invocation with `new`, but required. 15271 args.unshift(null); 15272 return new (Function.prototype.bind.apply(ctor, args))(); 15273 } 15274 15275 15276 return { 15277 invoke: invoke, 15278 instantiate: instantiate, 15279 get: getService, 15280 annotate: createInjector.$$annotate, 15281 has: function(name) { 15282 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); 15283 } 15284 }; 15285 } 15286} 15287 15288createInjector.$$annotate = annotate; 15289 15290/** 15291 * @ngdoc provider 15292 * @name $anchorScrollProvider 15293 * @this 15294 * 15295 * @description 15296 * Use `$anchorScrollProvider` to disable automatic scrolling whenever 15297 * {@link ng.$location#hash $location.hash()} changes. 15298 */ 15299function $AnchorScrollProvider() { 15300 15301 var autoScrollingEnabled = true; 15302 15303 /** 15304 * @ngdoc method 15305 * @name $anchorScrollProvider#disableAutoScrolling 15306 * 15307 * @description 15308 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to 15309 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br /> 15310 * Use this method to disable automatic scrolling. 15311 * 15312 * If automatic scrolling is disabled, one must explicitly call 15313 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the 15314 * current hash. 15315 */ 15316 this.disableAutoScrolling = function() { 15317 autoScrollingEnabled = false; 15318 }; 15319 15320 /** 15321 * @ngdoc service 15322 * @name $anchorScroll 15323 * @kind function 15324 * @requires $window 15325 * @requires $location 15326 * @requires $rootScope 15327 * 15328 * @description 15329 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the 15330 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified 15331 * in the 15332 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document). 15333 * 15334 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to 15335 * match any anchor whenever it changes. This can be disabled by calling 15336 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. 15337 * 15338 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a 15339 * vertical scroll-offset (either fixed or dynamic). 15340 * 15341 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of 15342 * {@link ng.$location#hash $location.hash()} will be used. 15343 * 15344 * @property {(number|function|jqLite)} yOffset 15345 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed 15346 * positioned elements at the top of the page, such as navbars, headers etc. 15347 * 15348 * `yOffset` can be specified in various ways: 15349 * - **number**: A fixed number of pixels to be used as offset.<br /><br /> 15350 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return 15351 * a number representing the offset (in pixels).<br /><br /> 15352 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from 15353 * the top of the page to the element's bottom will be used as offset.<br /> 15354 * **Note**: The element will be taken into account only as long as its `position` is set to 15355 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust 15356 * their height and/or positioning according to the viewport's size. 15357 * 15358 * <br /> 15359 * <div class="alert alert-warning"> 15360 * In order for `yOffset` to work properly, scrolling should take place on the document's root and 15361 * not some child element. 15362 * </div> 15363 * 15364 * @example 15365 <example module="anchorScrollExample" name="anchor-scroll"> 15366 <file name="index.html"> 15367 <div id="scrollArea" ng-controller="ScrollController"> 15368 <a ng-click="gotoBottom()">Go to bottom</a> 15369 <a id="bottom"></a> You're at the bottom! 15370 </div> 15371 </file> 15372 <file name="script.js"> 15373 angular.module('anchorScrollExample', []) 15374 .controller('ScrollController', ['$scope', '$location', '$anchorScroll', 15375 function($scope, $location, $anchorScroll) { 15376 $scope.gotoBottom = function() { 15377 // set the location.hash to the id of 15378 // the element you wish to scroll to. 15379 $location.hash('bottom'); 15380 15381 // call $anchorScroll() 15382 $anchorScroll(); 15383 }; 15384 }]); 15385 </file> 15386 <file name="style.css"> 15387 #scrollArea { 15388 height: 280px; 15389 overflow: auto; 15390 } 15391 15392 #bottom { 15393 display: block; 15394 margin-top: 2000px; 15395 } 15396 </file> 15397 </example> 15398 * 15399 * <hr /> 15400 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). 15401 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. 15402 * 15403 * @example 15404 <example module="anchorScrollOffsetExample" name="anchor-scroll-offset"> 15405 <file name="index.html"> 15406 <div class="fixed-header" ng-controller="headerCtrl"> 15407 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]"> 15408 Go to anchor {{x}} 15409 </a> 15410 </div> 15411 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]"> 15412 Anchor {{x}} of 5 15413 </div> 15414 </file> 15415 <file name="script.js"> 15416 angular.module('anchorScrollOffsetExample', []) 15417 .run(['$anchorScroll', function($anchorScroll) { 15418 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels 15419 }]) 15420 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', 15421 function($anchorScroll, $location, $scope) { 15422 $scope.gotoAnchor = function(x) { 15423 var newHash = 'anchor' + x; 15424 if ($location.hash() !== newHash) { 15425 // set the $location.hash to `newHash` and 15426 // $anchorScroll will automatically scroll to it 15427 $location.hash('anchor' + x); 15428 } else { 15429 // call $anchorScroll() explicitly, 15430 // since $location.hash hasn't changed 15431 $anchorScroll(); 15432 } 15433 }; 15434 } 15435 ]); 15436 </file> 15437 <file name="style.css"> 15438 body { 15439 padding-top: 50px; 15440 } 15441 15442 .anchor { 15443 border: 2px dashed DarkOrchid; 15444 padding: 10px 10px 200px 10px; 15445 } 15446 15447 .fixed-header { 15448 background-color: rgba(0, 0, 0, 0.2); 15449 height: 50px; 15450 position: fixed; 15451 top: 0; left: 0; right: 0; 15452 } 15453 15454 .fixed-header > a { 15455 display: inline-block; 15456 margin: 5px 15px; 15457 } 15458 </file> 15459 </example> 15460 */ 15461 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { 15462 var document = $window.document; 15463 15464 // Helper function to get first anchor from a NodeList 15465 // (using `Array#some()` instead of `angular#forEach()` since it's more performant 15466 // and working in all supported browsers.) 15467 function getFirstAnchor(list) { 15468 var result = null; 15469 Array.prototype.some.call(list, function(element) { 15470 if (nodeName_(element) === 'a') { 15471 result = element; 15472 return true; 15473 } 15474 }); 15475 return result; 15476 } 15477 15478 function getYOffset() { 15479 15480 var offset = scroll.yOffset; 15481 15482 if (isFunction(offset)) { 15483 offset = offset(); 15484 } else if (isElement(offset)) { 15485 var elem = offset[0]; 15486 var style = $window.getComputedStyle(elem); 15487 if (style.position !== 'fixed') { 15488 offset = 0; 15489 } else { 15490 offset = elem.getBoundingClientRect().bottom; 15491 } 15492 } else if (!isNumber(offset)) { 15493 offset = 0; 15494 } 15495 15496 return offset; 15497 } 15498 15499 function scrollTo(elem) { 15500 if (elem) { 15501 elem.scrollIntoView(); 15502 15503 var offset = getYOffset(); 15504 15505 if (offset) { 15506 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. 15507 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the 15508 // top of the viewport. 15509 // 15510 // IF the number of pixels from the top of `elem` to the end of the page's content is less 15511 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some 15512 // way down the page. 15513 // 15514 // This is often the case for elements near the bottom of the page. 15515 // 15516 // In such cases we do not need to scroll the whole `offset` up, just the difference between 15517 // the top of the element and the offset, which is enough to align the top of `elem` at the 15518 // desired position. 15519 var elemTop = elem.getBoundingClientRect().top; 15520 $window.scrollBy(0, elemTop - offset); 15521 } 15522 } else { 15523 $window.scrollTo(0, 0); 15524 } 15525 } 15526 15527 function scroll(hash) { 15528 // Allow numeric hashes 15529 hash = isString(hash) ? hash : isNumber(hash) ? hash.toString() : $location.hash(); 15530 var elm; 15531 15532 // empty hash, scroll to the top of the page 15533 if (!hash) scrollTo(null); 15534 15535 // element with given id 15536 else if ((elm = document.getElementById(hash))) scrollTo(elm); 15537 15538 // first anchor with given name :-D 15539 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); 15540 15541 // no element and hash === 'top', scroll to the top of the page 15542 else if (hash === 'top') scrollTo(null); 15543 } 15544 15545 // does not scroll when user clicks on anchor link that is currently on 15546 // (no url change, no $location.hash() change), browser native does scroll 15547 if (autoScrollingEnabled) { 15548 $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, 15549 function autoScrollWatchAction(newVal, oldVal) { 15550 // skip the initial scroll if $location.hash is empty 15551 if (newVal === oldVal && newVal === '') return; 15552 15553 jqLiteDocumentLoaded(function() { 15554 $rootScope.$evalAsync(scroll); 15555 }); 15556 }); 15557 } 15558 15559 return scroll; 15560 }]; 15561} 15562 15563var $animateMinErr = minErr('$animate'); 15564var ELEMENT_NODE = 1; 15565var NG_ANIMATE_CLASSNAME = 'ng-animate'; 15566 15567function mergeClasses(a,b) { 15568 if (!a && !b) return ''; 15569 if (!a) return b; 15570 if (!b) return a; 15571 if (isArray(a)) a = a.join(' '); 15572 if (isArray(b)) b = b.join(' '); 15573 return a + ' ' + b; 15574} 15575 15576function extractElementNode(element) { 15577 for (var i = 0; i < element.length; i++) { 15578 var elm = element[i]; 15579 if (elm.nodeType === ELEMENT_NODE) { 15580 return elm; 15581 } 15582 } 15583} 15584 15585function splitClasses(classes) { 15586 if (isString(classes)) { 15587 classes = classes.split(' '); 15588 } 15589 15590 // Use createMap() to prevent class assumptions involving property names in 15591 // Object.prototype 15592 var obj = createMap(); 15593 forEach(classes, function(klass) { 15594 // sometimes the split leaves empty string values 15595 // incase extra spaces were applied to the options 15596 if (klass.length) { 15597 obj[klass] = true; 15598 } 15599 }); 15600 return obj; 15601} 15602 15603// if any other type of options value besides an Object value is 15604// passed into the $animate.method() animation then this helper code 15605// will be run which will ignore it. While this patch is not the 15606// greatest solution to this, a lot of existing plugins depend on 15607// $animate to either call the callback (< 1.2) or return a promise 15608// that can be changed. This helper function ensures that the options 15609// are wiped clean incase a callback function is provided. 15610function prepareAnimateOptions(options) { 15611 return isObject(options) 15612 ? options 15613 : {}; 15614} 15615 15616var $$CoreAnimateJsProvider = /** @this */ function() { 15617 this.$get = noop; 15618}; 15619 15620// this is prefixed with Core since it conflicts with 15621// the animateQueueProvider defined in ngAnimate/animateQueue.js 15622var $$CoreAnimateQueueProvider = /** @this */ function() { 15623 var postDigestQueue = new NgMap(); 15624 var postDigestElements = []; 15625 15626 this.$get = ['$$AnimateRunner', '$rootScope', 15627 function($$AnimateRunner, $rootScope) { 15628 return { 15629 enabled: noop, 15630 on: noop, 15631 off: noop, 15632 pin: noop, 15633 15634 push: function(element, event, options, domOperation) { 15635 if (domOperation) { 15636 domOperation(); 15637 } 15638 15639 options = options || {}; 15640 if (options.from) { 15641 element.css(options.from); 15642 } 15643 if (options.to) { 15644 element.css(options.to); 15645 } 15646 15647 if (options.addClass || options.removeClass) { 15648 addRemoveClassesPostDigest(element, options.addClass, options.removeClass); 15649 } 15650 15651 var runner = new $$AnimateRunner(); 15652 15653 // since there are no animations to run the runner needs to be 15654 // notified that the animation call is complete. 15655 runner.complete(); 15656 return runner; 15657 } 15658 }; 15659 15660 15661 function updateData(data, classes, value) { 15662 var changed = false; 15663 if (classes) { 15664 classes = isString(classes) ? classes.split(' ') : 15665 isArray(classes) ? classes : []; 15666 forEach(classes, function(className) { 15667 if (className) { 15668 changed = true; 15669 data[className] = value; 15670 } 15671 }); 15672 } 15673 return changed; 15674 } 15675 15676 function handleCSSClassChanges() { 15677 forEach(postDigestElements, function(element) { 15678 var data = postDigestQueue.get(element); 15679 if (data) { 15680 var existing = splitClasses(element.attr('class')); 15681 var toAdd = ''; 15682 var toRemove = ''; 15683 forEach(data, function(status, className) { 15684 var hasClass = !!existing[className]; 15685 if (status !== hasClass) { 15686 if (status) { 15687 toAdd += (toAdd.length ? ' ' : '') + className; 15688 } else { 15689 toRemove += (toRemove.length ? ' ' : '') + className; 15690 } 15691 } 15692 }); 15693 15694 forEach(element, function(elm) { 15695 if (toAdd) { 15696 jqLiteAddClass(elm, toAdd); 15697 } 15698 if (toRemove) { 15699 jqLiteRemoveClass(elm, toRemove); 15700 } 15701 }); 15702 postDigestQueue.delete(element); 15703 } 15704 }); 15705 postDigestElements.length = 0; 15706 } 15707 15708 15709 function addRemoveClassesPostDigest(element, add, remove) { 15710 var data = postDigestQueue.get(element) || {}; 15711 15712 var classesAdded = updateData(data, add, true); 15713 var classesRemoved = updateData(data, remove, false); 15714 15715 if (classesAdded || classesRemoved) { 15716 15717 postDigestQueue.set(element, data); 15718 postDigestElements.push(element); 15719 15720 if (postDigestElements.length === 1) { 15721 $rootScope.$$postDigest(handleCSSClassChanges); 15722 } 15723 } 15724 } 15725 }]; 15726}; 15727 15728/** 15729 * @ngdoc provider 15730 * @name $animateProvider 15731 * 15732 * @description 15733 * Default implementation of $animate that doesn't perform any animations, instead just 15734 * synchronously performs DOM updates and resolves the returned runner promise. 15735 * 15736 * In order to enable animations the `ngAnimate` module has to be loaded. 15737 * 15738 * To see the functional implementation check out `src/ngAnimate/animate.js`. 15739 */ 15740var $AnimateProvider = ['$provide', /** @this */ function($provide) { 15741 var provider = this; 15742 var classNameFilter = null;
15743 15744 this.$$registeredAnimations = Object.create(null); 15745 15746 /** 15747 * @ngdoc method 15748 * @name $animateProvider#register 15749 * 15750 * @description 15751 * Registers a new injectable animation factory function. The factory function produces the 15752 * animation object which contains callback functions for each event that is expected to be 15753 * animated. 15754 * 15755 * * `eventFn`: `function(element, ... , doneFunction, options)` 15756 * The element to animate, the `doneFunction` and the options fed into the animation. Depending 15757 * on the type of animation additional arguments will be injected into the animation function. The 15758 * list below explains the function signatures for the different animation methods: 15759 *
15760 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options) 15761 * - addClass: function(element, addedClasses, doneFunction, options) 15762 * - removeClass: function(element, removedClasses, doneFunction, options) 15763 * - enter, leave, move: function(element, doneFunction, options) 15764 * - animate: function(element, fromStyles, toStyles, doneFunction, options) 15765 * 15766 * Make sure to trigger the `doneFunction` once the animation is fully complete. 15767 * 15768 * ```js 15769 * return { 15770 * //enter, leave, move signature 15771 * eventFn : function(element, done, options) { 15772 * //code to run the animation 15773 * //once complete, then run done() 15774 * return function endFunction(wasCancelled) { 15775 * //code to cancel the animation 15776 * } 15777 * } 15778 * } 15779 * ``` 15780 * 15781 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to). 15782 * @param {Function} factory The factory function that will be executed to return the animation 15783 * object. 15784 */ 15785 this.register = function(name, factory) { 15786 if (name && name.charAt(0) !== '.') { 15787 throw $animateMinErr('notcsel', 'Expecting class selector starting with \'.\' got \'{0}\'.', name); 15788 } 15789 15790 var key = name + '-animation'; 15791 provider.$$registeredAnimations[name.substr(1)] = key; 15792 $provide.factory(key, factory); 15793 }; 15794 15795 /** 15796 * @ngdoc method 15797 * @name $animateProvider#classNameFilter 15798 * 15799 * @description 15800 * Sets and/or returns the CSS class regular expression that is checked when performing 15801 * an animation. Upon bootstrap the classNameFilter value is not set at all and will 15802 * therefore enable $animate to attempt to perform an animation on any element that is triggered. 15803 * When setting the `classNameFilter` value, animations will only be performed on elements 15804 * that successfully match the filter expression. This in turn can boost performance 15805 * for low-powered devices as well as applications containing a lot of structural operations. 15806 * @param {RegExp=} expression The className expression which will be checked against all animations 15807 * @return {RegExp} The current CSS className expression value. If null then there is no expression value 15808 */ 15809 this.classNameFilter = function(expression) { 15810 if (arguments.length === 1) { 15811 classNameFilter = (expression instanceof RegExp) ? expression : null; 15812 if (classNameFilter) { 15813 var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]'); 15814 if (reservedRegex.test(classNameFilter.toString())) { 15815 classNameFilter = null; 15816 throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); 15817 } 15818 } 15819 } 15820 return classNameFilter; 15821 }; 15822 15823 this.$get = ['$$animateQueue', function($$animateQueue) { 15824 function domInsert(element, parentElement, afterElement) { 15825 // if for some reason the previous element was removed 15826 // from the dom sometime before this code runs then let's 15827 // just stick to using the parent element as the anchor 15828 if (afterElement) { 15829 var afterNode = extractElementNode(afterElement); 15830 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) { 15831 afterElement = null; 15832 } 15833 } 15834 if (afterElement) { 15835 afterElement.after(element); 15836 } else { 15837 parentElement.prepend(element); 15838 } 15839 } 15840 15841 /** 15842 * @ngdoc service 15843 * @name $animate 15844 * @description The $animate service exposes a series of DOM utility methods that provide support 15845 * for animation hooks. The default behavior is the application of DOM operations, however, 15846 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting 15847 * to ensure that animation runs with the triggered DOM operation. 15848 * 15849 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't 15850 * included and only when it is active then the animation hooks that `$animate` triggers will be 15851 * functional. Once active then all structural `ng-` directives will trigger animations as they perform 15852 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`, 15853 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations. 15854 * 15855 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives. 15856 * 15857 * To learn more about enabling animation support, click here to visit the 15858 * {@link ngAnimate ngAnimate module page}. 15859 */ 15860 return { 15861 // we don't call it directly since non-existant arguments may 15862 // be interpreted as null within the sub enabled function 15863 15864 /** 15865 * 15866 * @ngdoc method 15867 * @name $animate#on 15868 * @kind function
15869 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...) 15870 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback 15871 * is fired with the following params: 15872 * 15873 * ```js 15874 * $animate.on('enter', container, 15875 * function callback(element, phase) { 15876 * // cool we detected an enter animation within the container 15877 * } 15878 * ); 15879 * ``` 15880 * 15881 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...) 15882 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself 15883 * as well as among its children 15884 * @param {Function} callback the callback function that will be fired when the listener is triggered 15885 * 15886 * The arguments present in the callback function are: 15887 * * `element` - The captured DOM element that the animation was fired on. 15888 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends). 15889 */ 15890 on: $$animateQueue.on, 15891 15892 /** 15893 * 15894 * @ngdoc method 15895 * @name $animate#off 15896 * @kind function 15897 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method 15898 * can be used in three different ways depending on the arguments: 15899 * 15900 * ```js 15901 * // remove all the animation event listeners listening for `enter` 15902 * $animate.off('enter'); 15903 * 15904 * // remove listeners for all animation events from the container element 15905 * $animate.off(container); 15906 * 15907 * // remove all the animation event listeners listening for `enter` on the given element and its children 15908 * $animate.off('enter', container); 15909 * 15910 * // remove the event listener function provided by `callback` that is set 15911 * // to listen for `enter` on the given `container` as well as its children 15912 * $animate.off('enter', container, callback); 15913 * ``` 15914 * 15915 * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move, 15916 * addClass, removeClass, etc...), or the container element. If it is the element, all other 15917 * arguments are ignored. 15918 * @param {DOMElement=} container the container element the event listener was placed on 15919 * @param {Function=} callback the callback function that was registered as the listener 15920 */ 15921 off: $$animateQueue.off, 15922 15923 /** 15924 * @ngdoc method 15925 * @name $animate#pin 15926 * @kind function 15927 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists 15928 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the 15929 * element despite being outside the realm of the application or within another application. Say for example if the application 15930 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated 15931 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind 15932 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association. 15933 * 15934 * Note that this feature is only active when the `ngAnimate` module is used. 15935 * 15936 * @param {DOMElement} element the external element that will be pinned 15937 * @param {DOMElement} parentElement the host parent element that will be associated with the external element 15938 */ 15939 pin: $$animateQueue.pin, 15940 15941 /** 15942 * 15943 * @ngdoc method 15944 * @name $animate#enabled 15945 * @kind function 15946 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This 15947 * function can be called in four ways: 15948 * 15949 * ```js 15950 * // returns true or false 15951 * $animate.enabled(); 15952 * 15953 * // changes the enabled state for all animations 15954 * $animate.enabled(false); 15955 * $animate.enabled(true); 15956 * 15957 * // returns true or false if animations are enabled for an element 15958 * $animate.enabled(element); 15959 * 15960 * // changes the enabled state for an element and its children 15961 * $animate.enabled(element, true); 15962 * $animate.enabled(element, false); 15963 * ``` 15964 * 15965 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state 15966 * @param {boolean=} enabled whether or not the animations will be enabled for the element 15967 * 15968 * @return {boolean} whether or not animations are enabled 15969 */ 15970 enabled: $$animateQueue.enabled, 15971 15972 /** 15973 * @ngdoc method 15974 * @name $animate#cancel 15975 * @kind function
15976 * @description Cancels the provided animation. 15977 * 15978 * @param {Promise} animationPromise The animation promise that is returned when an animation is started. 15979 */ 15980 cancel: function(runner) { 15981 if (runner.end) { 15982 runner.end(); 15983 } 15984 }, 15985 15986 /** 15987 * 15988 * @ngdoc method 15989 * @name $animate#enter 15990 * @kind function 15991 * @description Inserts the element into the DOM either after the `after` element (if provided) or 15992 * as the first child within the `parent` element and then triggers an animation. 15993 * A promise is returned that will be resolved during the next digest once the animation 15994 * has completed. 15995 * 15996 * @param {DOMElement} element the element which will be inserted into the DOM 15997 * @param {DOMElement} parent the parent element which will append the element as 15998 * a child (so long as the after element is not present) 15999 * @param {DOMElement=} after the sibling element after which the element will be appended 16000 * @param {object=} options an optional collection of options/styles that will be applied to the element. 16001 * The object can have the following properties: 16002 * 16003 * - **addClass** - `{string}` - space-separated CSS classes to add to element 16004 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` 16005 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element 16006 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` 16007 * 16008 * @return {Promise} the animation callback promise 16009 */ 16010 enter: function(element, parent, after, options) { 16011 parent = parent && jqLite(parent); 16012 after = after && jqLite(after); 16013 parent = parent || after.parent(); 16014 domInsert(element, parent, after); 16015 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options)); 16016 }, 16017 16018 /** 16019 * 16020 * @ngdoc method 16021 * @name $animate#move 16022 * @kind function 16023 * @description Inserts (moves) the element into its new position in the DOM either after 16024 * the `after` element (if provided) or as the first child within the `parent` element 16025 * and then triggers an animation. A promise is returned that will be resolved 16026 * during the next digest once the animation has completed. 16027 * 16028 * @param {DOMElement} element the element which will be moved into the new DOM position 16029 * @param {DOMElement} parent the parent element which will append the element as 16030 * a child (so long as the after element is not present) 16031 * @param {DOMElement=} after the sibling element after which the element will be appended 16032 * @param {object=} options an optional collection of options/styles that will be applied to the element. 16033 * The object can have the following properties: 16034 * 16035 * - **addClass** - `{string}` - space-separated CSS classes to add to element 16036 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` 16037 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element 16038 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` 16039 * 16040 * @return {Promise} the animation callback promise 16041 */ 16042 move: function(element, parent, after, options) { 16043 parent = parent && jqLite(parent); 16044 after = after && jqLite(after); 16045 parent = parent || after.parent(); 16046 domInsert(element, parent, after); 16047 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options)); 16048 }, 16049 16050 /** 16051 * @ngdoc method 16052 * @name $animate#leave 16053 * @kind function 16054 * @description Triggers an animation and then removes the element from the DOM. 16055 * When the function is called a promise is returned that will be resolved during the next 16056 * digest once the animation has completed. 16057 * 16058 * @param {DOMElement} element the element which will be removed from the DOM 16059 * @param {object=} options an optional collection of options/styles that will be applied to the element. 16060 * The object can have the following properties: 16061 * 16062 * - **addClass** - `{string}` - space-separated CSS classes to add to element 16063 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` 16064 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element 16065 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` 16066 * 16067 * @return {Promise} the animation callback promise 16068 */ 16069 leave: function(element, options) { 16070 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() { 16071 element.remove(); 16072 }); 16073 }, 16074 16075 /** 16076 * @ngdoc method 16077 * @name $animate#addClass 16078 * @kind function 16079 *
16080 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon 16081 * execution, the addClass operation will only be handled after the next digest and it will not trigger an 16082 * animation if element already contains the CSS class or if the class is removed at a later step. 16083 * Note that class-based animations are treated differently compared to structural animations 16084 * (like enter, move and leave) since the CSS classes may be added/removed at different points 16085 * depending if CSS or JavaScript animations are used. 16086 * 16087 * @param {DOMElement} element the element which the CSS classes will be applied to 16088 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces) 16089 * @param {object=} options an optional collection of options/styles that will be applied to the element. 16090 * The object can have the following properties: 16091 * 16092 * - **addClass** - `{string}` - space-separated CSS classes to add to element 16093 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` 16094 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element 16095 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` 16096 * 16097 * @return {Promise} the animation callback promise 16098 */ 16099 addClass: function(element, className, options) { 16100 options = prepareAnimateOptions(options); 16101 options.addClass = mergeClasses(options.addclass, className); 16102 return $$animateQueue.push(element, 'addClass', options); 16103 }, 16104 16105 /** 16106 * @ngdoc method 16107 * @name $animate#removeClass 16108 * @kind function 16109 * 16110 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon 16111 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an 16112 * animation if element does not contain the CSS class or if the class is added at a later step. 16113 * Note that class-based animations are treated differently compared to structural animations 16114 * (like enter, move and leave) since the CSS classes may be added/removed at different points 16115 * depending if CSS or JavaScript animations are used. 16116 * 16117 * @param {DOMElement} element the element which the CSS classes will be applied to 16118 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces) 16119 * @param {object=} options an optional collection of options/styles that will be applied to the element. 16120 * The object can have the following properties: 16121 * 16122 * - **addClass** - `{string}` - space-separated CSS classes to add to element 16123 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` 16124 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element 16125 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` 16126 * 16127 * @return {Promise} the animation callback promise 16128 */ 16129 removeClass: function(element, className, options) { 16130 options = prepareAnimateOptions(options); 16131 options.removeClass = mergeClasses(options.removeClass, className); 16132 return $$animateQueue.push(element, 'removeClass', options); 16133 }, 16134 16135 /** 16136 * @ngdoc method 16137 * @name $animate#setClass 16138 * @kind function 16139 * 16140 * @description Performs both the addition and removal of a CSS classes on an element and (during the process) 16141 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and 16142 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has 16143 * passed. Note that class-based animations are treated differently compared to structural animations 16144 * (like enter, move and leave) since the CSS classes may be added/removed at different points 16145 * depending if CSS or JavaScript animations are used. 16146 * 16147 * @param {DOMElement} element the element which the CSS classes will be applied to 16148 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces) 16149 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces) 16150 * @param {object=} options an optional collection of options/styles that will be applied to the element. 16151 * The object can have the following properties: 16152 * 16153 * - **addClass** - `{string}` - space-separated CSS classes to add to element 16154 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` 16155 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element 16156 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` 16157 * 16158 * @return {Promise} the animation callback promise 16159 */ 16160 setClass: function(element, add, remove, options) { 16161 options = prepareAnimateOptions(options); 16162 options.addClass = mergeClasses(options.addClass, add); 16163 options.removeClass = mergeClasses(options.removeClass, remove); 16164 return $$animateQueue.push(element, 'setClass', options); 16165 }, 16166 16167 /** 16168 * @ngdoc method 16169 * @name $animate#animate 16170 * @kind function 16171 *
16172 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element. 16173 * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take 16174 * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and 16175 * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding 16176 * style in `to`, the style in `from` is applied immediately, and no animation is run. 16177 * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate` 16178 * method (or as part of the `options` parameter): 16179 * 16180 * ```js 16181 * ngModule.animation('.my-inline-animation', function() { 16182 * return { 16183 * animate : function(element, from, to, done, options) { 16184 * //animation 16185 * done(); 16186 * } 16187 * } 16188 * }); 16189 * ``` 16190 * 16191 * @param {DOMElement} element the element which the CSS styles will be applied to 16192 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation. 16193 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation. 16194 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If 16195 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element. 16196 * (Note that if no animation is detected then this value will not be applied to the element.) 16197 * @param {object=} options an optional collection of options/styles that will be applied to the element. 16198 * The object can have the following properties: 16199 * 16200 * - **addClass** - `{string}` - space-separated CSS classes to add to element 16201 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` 16202 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element 16203 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` 16204 * 16205 * @return {Promise} the animation callback promise 16206 */ 16207 animate: function(element, from, to, className, options) { 16208 options = prepareAnimateOptions(options); 16209 options.from = options.from ? extend(options.from, from) : from; 16210 options.to = options.to ? extend(options.to, to) : to; 16211 16212 className = className || 'ng-inline-animate'; 16213 options.tempClasses = mergeClasses(options.tempClasses, className); 16214 return $$animateQueue.push(element, 'animate', options); 16215 } 16216 }; 16217 }]; 16218}]; 16219 16220var $$AnimateAsyncRunFactoryProvider = /** @this */ function() { 16221 this.$get = ['$$rAF', function($$rAF) { 16222 var waitQueue = []; 16223 16224 function waitForTick(fn) { 16225 waitQueue.push(fn); 16226 if (waitQueue.length > 1) return; 16227 $$rAF(function() { 16228 for (var i = 0; i < waitQueue.length; i++) { 16229 waitQueue[i](); 16230 } 16231 waitQueue = []; 16232 }); 16233 } 16234 16235 return function() { 16236 var passed = false; 16237 waitForTick(function() { 16238 passed = true; 16239 }); 16240 return function(callback) { 16241 if (passed) { 16242 callback(); 16243 } else { 16244 waitForTick(callback); 16245 } 16246 }; 16247 }; 16248 }]; 16249}; 16250 16251var $$AnimateRunnerFactoryProvider = /** @this */ function() { 16252 this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$$isDocumentHidden', '$timeout', 16253 function($q, $sniffer, $$animateAsyncRun, $$isDocumentHidden, $timeout) { 16254 16255 var INITIAL_STATE = 0; 16256 var DONE_PENDING_STATE = 1; 16257 var DONE_COMPLETE_STATE = 2; 16258 16259 AnimateRunner.chain = function(chain, callback) { 16260 var index = 0; 16261 16262 next(); 16263 function next() { 16264 if (index === chain.length) { 16265 callback(true); 16266 return; 16267 } 16268 16269 chain[index](function(response) { 16270 if (response === false) { 16271 callback(false); 16272 return; 16273 } 16274 index++; 16275 next(); 16276 }); 16277 } 16278 }; 16279 16280 AnimateRunner.all = function(runners, callback) { 16281 var count = 0; 16282 var status = true;
vendor: 5,133 bytes, lines 16283-16495
16283 forEach(runners, function(runner) { 16284 runner.done(onProgress); 16285 }); 16286 16287 function onProgress(response) { 16288 status = status && response; 16289 if (++count === runners.length) { 16290 callback(status); 16291 } 16292 } 16293 }; 16294 16295 function AnimateRunner(host) { 16296 this.setHost(host); 16297 16298 var rafTick = $$animateAsyncRun(); 16299 var timeoutTick = function(fn) { 16300 $timeout(fn, 0, false); 16301 }; 16302 16303 this._doneCallbacks = []; 16304 this._tick = function(fn) { 16305 if ($$isDocumentHidden()) { 16306 timeoutTick(fn); 16307 } else { 16308 rafTick(fn); 16309 } 16310 }; 16311 this._state = 0; 16312 } 16313 16314 AnimateRunner.prototype = { 16315 setHost: function(host) { 16316 this.host = host || {}; 16317 }, 16318 16319 done: function(fn) { 16320 if (this._state === DONE_COMPLETE_STATE) { 16321 fn(); 16322 } else { 16323 this._doneCallbacks.push(fn); 16324 } 16325 }, 16326 16327 progress: noop, 16328 16329 getPromise: function() { 16330 if (!this.promise) { 16331 var self = this; 16332 this.promise = $q(function(resolve, reject) { 16333 self.done(function(status) { 16334 if (status === false) { 16335 reject(); 16336 } else { 16337 resolve(); 16338 } 16339 }); 16340 }); 16341 } 16342 return this.promise; 16343 }, 16344 16345 then: function(resolveHandler, rejectHandler) { 16346 return this.getPromise().then(resolveHandler, rejectHandler); 16347 }, 16348 16349 'catch': function(handler) { 16350 return this.getPromise()['catch'](handler); 16351 }, 16352 16353 'finally': function(handler) { 16354 return this.getPromise()['finally'](handler); 16355 }, 16356 16357 pause: function() { 16358 if (this.host.pause) { 16359 this.host.pause(); 16360 } 16361 }, 16362 16363 resume: function() { 16364 if (this.host.resume) { 16365 this.host.resume(); 16366 } 16367 }, 16368 16369 end: function() { 16370 if (this.host.end) { 16371 this.host.end(); 16372 } 16373 this._resolve(true); 16374 }, 16375 16376 cancel: function() { 16377 if (this.host.cancel) { 16378 this.host.cancel(); 16379 } 16380 this._resolve(false); 16381 }, 16382 16383 complete: function(response) { 16384 var self = this; 16385 if (self._state === INITIAL_STATE) { 16386 self._state = DONE_PENDING_STATE; 16387 self._tick(function() { 16388 self._resolve(response); 16389 }); 16390 } 16391 }, 16392 16393 _resolve: function(response) { 16394 if (this._state !== DONE_COMPLETE_STATE) { 16395 forEach(this._doneCallbacks, function(fn) { 16396 fn(response); 16397 }); 16398 this._doneCallbacks.length = 0; 16399 this._state = DONE_COMPLETE_STATE; 16400 } 16401 } 16402 }; 16403 16404 return AnimateRunner; 16405 }]; 16406}; 16407 16408/* exported $CoreAnimateCssProvider */ 16409 16410/** 16411 * @ngdoc service 16412 * @name $animateCss 16413 * @kind object 16414 * @this 16415 * 16416 * @description 16417 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included, 16418 * then the `$animateCss` service will actually perform animations. 16419 * 16420 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}. 16421 */ 16422var $CoreAnimateCssProvider = function() { 16423 this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) { 16424 16425 return function(element, initialOptions) { 16426 // all of the animation functions should create 16427 // a copy of the options data, however, if a 16428 // parent service has already created a copy then 16429 // we should stick to using that 16430 var options = initialOptions || {}; 16431 if (!options.$$prepared) { 16432 options = copy(options); 16433 } 16434 16435 // there is no point in applying the styles since 16436 // there is no animation that goes on at all in 16437 // this version of $animateCss. 16438 if (options.cleanupStyles) { 16439 options.from = options.to = null; 16440 } 16441 16442 if (options.from) { 16443 element.css(options.from); 16444 options.from = null; 16445 } 16446 16447 var closed, runner = new $$AnimateRunner(); 16448 return { 16449 start: run, 16450 end: run 16451 }; 16452 16453 function run() { 16454 $$rAF(function() { 16455 applyAnimationContents(); 16456 if (!closed) { 16457 runner.complete(); 16458 } 16459 closed = true; 16460 }); 16461 return runner; 16462 } 16463 16464 function applyAnimationContents() { 16465 if (options.addClass) { 16466 element.addClass(options.addClass); 16467 options.addClass = null; 16468 } 16469 if (options.removeClass) { 16470 element.removeClass(options.removeClass); 16471 options.removeClass = null; 16472 } 16473 if (options.to) { 16474 element.css(options.to); 16475 options.to = null; 16476 } 16477 } 16478 }; 16479 }]; 16480}; 16481 16482/* global stripHash: true */ 16483 16484/** 16485 * ! This is a private undocumented service ! 16486 * 16487 * @name $browser 16488 * @requires $log 16489 * @description 16490 * This object has two goals: 16491 * 16492 * - hide all the global state in the browser caused by the window object 16493 * - abstract away all the browser specific features and inconsistencies 16494 * 16495 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
16496 * service, which can be used for convenient testing of the application without the interaction with 16497 * the real browser apis. 16498 */ 16499/** 16500 * @param {object} window The global window object. 16501 * @param {object} document jQuery wrapped document. 16502 * @param {object} $log window.console or an object with the same interface. 16503 * @param {object} $sniffer $sniffer service 16504 */ 16505function Browser(window, document, $log, $sniffer) { 16506 var self = this, 16507 location = window.location, 16508 history = window.history, 16509 setTimeout = window.setTimeout, 16510 clearTimeout = window.clearTimeout, 16511 pendingDeferIds = {}; 16512 16513 self.isMock = false; 16514 16515 var outstandingRequestCount = 0; 16516 var outstandingRequestCallbacks = []; 16517 16518 // TODO(vojta): remove this temporary api 16519 self.$$completeOutstandingRequest = completeOutstandingRequest; 16520 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; 16521 16522 /** 16523 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` 16524 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. 16525 */ 16526 function completeOutstandingRequest(fn) { 16527 try { 16528 fn.apply(null, sliceArgs(arguments, 1)); 16529 } finally { 16530 outstandingRequestCount--; 16531 if (outstandingRequestCount === 0) { 16532 while (outstandingRequestCallbacks.length) { 16533 try { 16534 outstandingRequestCallbacks.pop()(); 16535 } catch (e) { 16536 $log.error(e); 16537 } 16538 } 16539 } 16540 } 16541 } 16542 16543 function getHash(url) { 16544 var index = url.indexOf('#'); 16545 return index === -1 ? '' : url.substr(index); 16546 } 16547 16548 /** 16549 * @private 16550 * Note: this method is used only by scenario runner 16551 * TODO(vojta): prefix this method with $$ ? 16552 * @param {function()} callback Function that will be called when no outstanding request 16553 */ 16554 self.notifyWhenNoOutstandingRequests = function(callback) { 16555 if (outstandingRequestCount === 0) { 16556 callback(); 16557 } else { 16558 outstandingRequestCallbacks.push(callback); 16559 } 16560 }; 16561 16562 ////////////////////////////////////////////////////////////// 16563 // URL API 16564 ////////////////////////////////////////////////////////////// 16565 16566 var cachedState, lastHistoryState, 16567 lastBrowserUrl = location.href, 16568 baseElement = document.find('base'), 16569 pendingLocation = null, 16570 getCurrentState = !$sniffer.history ? noop : function getCurrentState() { 16571 try { 16572 return history.state; 16573 } catch (e) { 16574 // MSIE can reportedly throw when there is no state (UNCONFIRMED). 16575 } 16576 }; 16577 16578 cacheState(); 16579 16580 /** 16581 * @name $browser#url 16582 * 16583 * @description 16584 * GETTER: 16585 * Without any argument, this method just returns current value of location.href. 16586 * 16587 * SETTER: 16588 * With at least one argument, this method sets url to new value. 16589 * If html5 history api supported, pushState/replaceState is used, otherwise 16590 * location.href/location.replace is used. 16591 * Returns its own instance to allow chaining 16592 * 16593 * NOTE: this api is intended for use only by the $location service. Please use the 16594 * {@link ng.$location $location service} to change url. 16595 * 16596 * @param {string} url New url (when used as setter) 16597 * @param {boolean=} replace Should new url replace current history record? 16598 * @param {object=} state object to use with pushState/replaceState 16599 */ 16600 self.url = function(url, replace, state) { 16601 // In modern browsers `history.state` is `null` by default; treating it separately 16602 // from `undefined` would cause `$browser.url('/foo')` to change `history.state` 16603 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here. 16604 if (isUndefined(state)) { 16605 state = null; 16606 } 16607 16608 // Android Browser BFCache causes location, history reference to become stale. 16609 if (location !== window.location) location = window.location; 16610 if (history !== window.history) history = window.history; 16611 16612 // setter 16613 if (url) { 16614 var sameState = lastHistoryState === state; 16615 16616 // Don't change anything if previous and current URLs and states match. This also prevents 16617 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. 16618 // See https://github.com/angular/angular.js/commit/ffb2701 16619 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { 16620 return self; 16621 } 16622 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); 16623 lastBrowserUrl = url; 16624 lastHistoryState = state; 16625 // Don't use history API if only the hash changed 16626 // due to a bug in IE10/IE11 which leads 16627 // to not firing a `hashchange` nor `popstate` event 16628 // in some cases (see #9143). 16629 if ($sniffer.history && (!sameBase || !sameState)) { 16630 history[replace ? 'replaceState' : 'pushState'](state, '', url); 16631 cacheState(); 16632 } else { 16633 if (!sameBase) { 16634 pendingLocation = url; 16635 } 16636 if (replace) { 16637 location.replace(url); 16638 } else if (!sameBase) { 16639 location.href = url; 16640 } else { 16641 location.hash = getHash(url); 16642 } 16643 if (location.href !== url) { 16644 pendingLocation = url; 16645 } 16646 } 16647 if (pendingLocation) { 16648 pendingLocation = url; 16649 } 16650 return self; 16651 // getter 16652 } else { 16653 // - pendingLocation is needed as browsers don't allow to read out 16654 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see 16655 // https://openradar.appspot.com/22186109). 16656 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 16657 return pendingLocation || location.href.replace(/%27/g,'\''); 16658 } 16659 }; 16660 16661 /** 16662 * @name $browser#state 16663 * 16664 * @description 16665 * This method is a getter. 16666 * 16667 * Return history.state or null if history.state is undefined. 16668 * 16669 * @returns {object} state 16670 */ 16671 self.state = function() { 16672 return cachedState; 16673 }; 16674 16675 var urlChangeListeners = [], 16676 urlChangeInit = false;
vendor: 16,994 bytes, lines 16677-17242
16677 16678 function cacheStateAndFireUrlChange() { 16679 pendingLocation = null; 16680 fireStateOrUrlChange(); 16681 } 16682 16683 // This variable should be used *only* inside the cacheState function. 16684 var lastCachedState = null; 16685 function cacheState() { 16686 // This should be the only place in $browser where `history.state` is read. 16687 cachedState = getCurrentState(); 16688 cachedState = isUndefined(cachedState) ? null : cachedState; 16689 16690 // Prevent callbacks fo fire twice if both hashchange & popstate were fired. 16691 if (equals(cachedState, lastCachedState)) { 16692 cachedState = lastCachedState; 16693 } 16694 16695 lastCachedState = cachedState; 16696 lastHistoryState = cachedState; 16697 } 16698 16699 function fireStateOrUrlChange() { 16700 var prevLastHistoryState = lastHistoryState; 16701 cacheState(); 16702 16703 if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) { 16704 return; 16705 } 16706 16707 lastBrowserUrl = self.url(); 16708 lastHistoryState = cachedState; 16709 forEach(urlChangeListeners, function(listener) { 16710 listener(self.url(), cachedState); 16711 }); 16712 } 16713 16714 /** 16715 * @name $browser#onUrlChange 16716 * 16717 * @description 16718 * Register callback function that will be called, when url changes. 16719 * 16720 * It's only called when the url is changed from outside of angular: 16721 * - user types different url into address bar 16722 * - user clicks on history (forward/back) button 16723 * - user clicks on a link 16724 * 16725 * It's not called when url is changed by $browser.url() method 16726 * 16727 * The listener gets called with new url as parameter. 16728 * 16729 * NOTE: this api is intended for use only by the $location service. Please use the 16730 * {@link ng.$location $location service} to monitor url changes in angular apps. 16731 * 16732 * @param {function(string)} listener Listener function to be called when url changes. 16733 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. 16734 */ 16735 self.onUrlChange = function(callback) { 16736 // TODO(vojta): refactor to use node's syntax for events 16737 if (!urlChangeInit) { 16738 // We listen on both (hashchange/popstate) when available, as some browsers don't 16739 // fire popstate when user changes the address bar and don't fire hashchange when url 16740 // changed by push/replaceState 16741 16742 // html5 history api - popstate event 16743 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange); 16744 // hashchange event 16745 jqLite(window).on('hashchange', cacheStateAndFireUrlChange); 16746 16747 urlChangeInit = true; 16748 } 16749 16750 urlChangeListeners.push(callback); 16751 return callback; 16752 }; 16753 16754 /** 16755 * @private 16756 * Remove popstate and hashchange handler from window. 16757 * 16758 * NOTE: this api is intended for use only by $rootScope. 16759 */ 16760 self.$$applicationDestroyed = function() { 16761 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange); 16762 }; 16763 16764 /** 16765 * Checks whether the url has changed outside of Angular. 16766 * Needs to be exported to be able to check for changes that have been done in sync, 16767 * as hashchange/popstate events fire in async. 16768 */ 16769 self.$$checkUrlChange = fireStateOrUrlChange; 16770 16771 ////////////////////////////////////////////////////////////// 16772 // Misc API 16773 ////////////////////////////////////////////////////////////// 16774 16775 /** 16776 * @name $browser#baseHref 16777 * 16778 * @description 16779 * Returns current <base href> 16780 * (always relative - without domain) 16781 * 16782 * @returns {string} The current base href 16783 */ 16784 self.baseHref = function() { 16785 var href = baseElement.attr('href'); 16786 return href ? href.replace(/^(https?:)?\/\/[^/]*/, '') : ''; 16787 }; 16788 16789 /** 16790 * @name $browser#defer 16791 * @param {function()} fn A function, who's execution should be deferred. 16792 * @param {number=} [delay=0] of milliseconds to defer the function execution. 16793 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. 16794 * 16795 * @description 16796 * Executes a fn asynchronously via `setTimeout(fn, delay)`. 16797 * 16798 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using 16799 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed 16800 * via `$browser.defer.flush()`. 16801 * 16802 */ 16803 self.defer = function(fn, delay) { 16804 var timeoutId; 16805 outstandingRequestCount++; 16806 timeoutId = setTimeout(function() { 16807 delete pendingDeferIds[timeoutId]; 16808 completeOutstandingRequest(fn); 16809 }, delay || 0); 16810 pendingDeferIds[timeoutId] = true; 16811 return timeoutId; 16812 }; 16813 16814 16815 /** 16816 * @name $browser#defer.cancel 16817 * 16818 * @description 16819 * Cancels a deferred task identified with `deferId`. 16820 * 16821 * @param {*} deferId Token returned by the `$browser.defer` function. 16822 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully 16823 * canceled. 16824 */ 16825 self.defer.cancel = function(deferId) { 16826 if (pendingDeferIds[deferId]) { 16827 delete pendingDeferIds[deferId]; 16828 clearTimeout(deferId); 16829 completeOutstandingRequest(noop); 16830 return true; 16831 } 16832 return false; 16833 }; 16834 16835} 16836 16837/** @this */ 16838function $BrowserProvider() { 16839 this.$get = ['$window', '$log', '$sniffer', '$document', 16840 function($window, $log, $sniffer, $document) { 16841 return new Browser($window, $document, $log, $sniffer); 16842 }]; 16843} 16844 16845/** 16846 * @ngdoc service 16847 * @name $cacheFactory 16848 * @this 16849 * 16850 * @description 16851 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to 16852 * them. 16853 * 16854 * ```js 16855 * 16856 * var cache = $cacheFactory('cacheId'); 16857 * expect($cacheFactory.get('cacheId')).toBe(cache); 16858 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); 16859 * 16860 * cache.put("key", "value"); 16861 * cache.put("another key", "another value"); 16862 * 16863 * // We've specified no options on creation 16864 * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); 16865 * 16866 * ``` 16867 * 16868 * 16869 * @param {string} cacheId Name or id of the newly created cache. 16870 * @param {object=} options Options object that specifies the cache behavior. Properties: 16871 * 16872 * - `{number=}` `capacity` — turns the cache into LRU cache. 16873 * 16874 * @returns {object} Newly created cache object with the following set of methods: 16875 * 16876 * - `{object}` `info()` — Returns id, size, and options of cache. 16877 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns 16878 * it. 16879 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. 16880 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. 16881 * - `{void}` `removeAll()` — Removes all cached values. 16882 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. 16883 * 16884 * @example 16885 <example module="cacheExampleApp" name="cache-factory"> 16886 <file name="index.html"> 16887 <div ng-controller="CacheController"> 16888 <input ng-model="newCacheKey" placeholder="Key"> 16889 <input ng-model="newCacheValue" placeholder="Value"> 16890 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button> 16891 16892 <p ng-if="keys.length">Cached Values</p> 16893 <div ng-repeat="key in keys"> 16894 <span ng-bind="key"></span> 16895 <span>: </span> 16896 <b ng-bind="cache.get(key)"></b> 16897 </div> 16898 16899 <p>Cache Info</p> 16900 <div ng-repeat="(key, value) in cache.info()"> 16901 <span ng-bind="key"></span> 16902 <span>: </span> 16903 <b ng-bind="value"></b> 16904 </div> 16905 </div> 16906 </file> 16907 <file name="script.js"> 16908 angular.module('cacheExampleApp', []). 16909 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { 16910 $scope.keys = []; 16911 $scope.cache = $cacheFactory('cacheId'); 16912 $scope.put = function(key, value) { 16913 if (angular.isUndefined($scope.cache.get(key))) { 16914 $scope.keys.push(key); 16915 } 16916 $scope.cache.put(key, angular.isUndefined(value) ? null : value); 16917 }; 16918 }]); 16919 </file> 16920 <file name="style.css"> 16921 p { 16922 margin: 10px 0 3px; 16923 } 16924 </file> 16925 </example> 16926 */ 16927function $CacheFactoryProvider() { 16928 16929 this.$get = function() { 16930 var caches = {}; 16931 16932 function cacheFactory(cacheId, options) { 16933 if (cacheId in caches) { 16934 throw minErr('$cacheFactory')('iid', 'CacheId \'{0}\' is already taken!', cacheId); 16935 } 16936 16937 var size = 0, 16938 stats = extend({}, options, {id: cacheId}), 16939 data = createMap(), 16940 capacity = (options && options.capacity) || Number.MAX_VALUE, 16941 lruHash = createMap(), 16942 freshEnd = null, 16943 staleEnd = null; 16944 16945 /** 16946 * @ngdoc type 16947 * @name $cacheFactory.Cache 16948 * 16949 * @description 16950 * A cache object used to store and retrieve data, primarily used by 16951 * {@link $http $http} and the {@link ng.directive:script script} directive to cache 16952 * templates and other data. 16953 * 16954 * ```js 16955 * angular.module('superCache') 16956 * .factory('superCache', ['$cacheFactory', function($cacheFactory) { 16957 * return $cacheFactory('super-cache'); 16958 * }]); 16959 * ``` 16960 * 16961 * Example test: 16962 * 16963 * ```js 16964 * it('should behave like a cache', inject(function(superCache) { 16965 * superCache.put('key', 'value'); 16966 * superCache.put('another key', 'another value'); 16967 * 16968 * expect(superCache.info()).toEqual({ 16969 * id: 'super-cache', 16970 * size: 2 16971 * }); 16972 * 16973 * superCache.remove('another key'); 16974 * expect(superCache.get('another key')).toBeUndefined(); 16975 * 16976 * superCache.removeAll(); 16977 * expect(superCache.info()).toEqual({ 16978 * id: 'super-cache', 16979 * size: 0 16980 * }); 16981 * })); 16982 * ``` 16983 */ 16984 return (caches[cacheId] = { 16985 16986 /** 16987 * @ngdoc method 16988 * @name $cacheFactory.Cache#put 16989 * @kind function 16990 * 16991 * @description 16992 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be 16993 * retrieved later, and incrementing the size of the cache if the key was not already 16994 * present in the cache. If behaving like an LRU cache, it will also remove stale 16995 * entries from the set. 16996 * 16997 * It will not insert undefined values into the cache. 16998 * 16999 * @param {string} key the key under which the cached data is stored. 17000 * @param {*} value the value to store alongside the key. If it is undefined, the key 17001 * will not be stored. 17002 * @returns {*} the value stored. 17003 */ 17004 put: function(key, value) { 17005 if (isUndefined(value)) return; 17006 if (capacity < Number.MAX_VALUE) { 17007 var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); 17008 17009 refresh(lruEntry); 17010 } 17011 17012 if (!(key in data)) size++; 17013 data[key] = value; 17014 17015 if (size > capacity) { 17016 this.remove(staleEnd.key); 17017 } 17018 17019 return value; 17020 }, 17021 17022 /** 17023 * @ngdoc method 17024 * @name $cacheFactory.Cache#get 17025 * @kind function 17026 * 17027 * @description 17028 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. 17029 * 17030 * @param {string} key the key of the data to be retrieved 17031 * @returns {*} the value stored. 17032 */ 17033 get: function(key) { 17034 if (capacity < Number.MAX_VALUE) { 17035 var lruEntry = lruHash[key]; 17036 17037 if (!lruEntry) return; 17038 17039 refresh(lruEntry); 17040 } 17041 17042 return data[key]; 17043 }, 17044 17045 17046 /** 17047 * @ngdoc method 17048 * @name $cacheFactory.Cache#remove 17049 * @kind function 17050 * 17051 * @description 17052 * Removes an entry from the {@link $cacheFactory.Cache Cache} object. 17053 * 17054 * @param {string} key the key of the entry to be removed 17055 */ 17056 remove: function(key) { 17057 if (capacity < Number.MAX_VALUE) { 17058 var lruEntry = lruHash[key]; 17059 17060 if (!lruEntry) return; 17061 17062 if (lruEntry === freshEnd) freshEnd = lruEntry.p; 17063 if (lruEntry === staleEnd) staleEnd = lruEntry.n; 17064 link(lruEntry.n,lruEntry.p); 17065 17066 delete lruHash[key]; 17067 } 17068 17069 if (!(key in data)) return; 17070 17071 delete data[key]; 17072 size--; 17073 }, 17074 17075 17076 /** 17077 * @ngdoc method 17078 * @name $cacheFactory.Cache#removeAll 17079 * @kind function 17080 * 17081 * @description 17082 * Clears the cache object of any entries. 17083 */ 17084 removeAll: function() { 17085 data = createMap(); 17086 size = 0; 17087 lruHash = createMap(); 17088 freshEnd = staleEnd = null; 17089 }, 17090 17091 17092 /** 17093 * @ngdoc method 17094 * @name $cacheFactory.Cache#destroy 17095 * @kind function 17096 * 17097 * @description 17098 * Destroys the {@link $cacheFactory.Cache Cache} object entirely, 17099 * removing it from the {@link $cacheFactory $cacheFactory} set. 17100 */ 17101 destroy: function() { 17102 data = null; 17103 stats = null; 17104 lruHash = null; 17105 delete caches[cacheId]; 17106 }, 17107 17108 17109 /** 17110 * @ngdoc method 17111 * @name $cacheFactory.Cache#info 17112 * @kind function 17113 * 17114 * @description 17115 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. 17116 * 17117 * @returns {object} an object with the following properties: 17118 * <ul> 17119 * <li>**id**: the id of the cache instance</li> 17120 * <li>**size**: the number of entries kept in the cache instance</li> 17121 * <li>**...**: any additional properties from the options object when creating the 17122 * cache.</li> 17123 * </ul> 17124 */ 17125 info: function() { 17126 return extend({}, stats, {size: size}); 17127 } 17128 }); 17129 17130 17131 /** 17132 * makes the `entry` the freshEnd of the LRU linked list 17133 */ 17134 function refresh(entry) { 17135 if (entry !== freshEnd) { 17136 if (!staleEnd) { 17137 staleEnd = entry; 17138 } else if (staleEnd === entry) { 17139 staleEnd = entry.n; 17140 } 17141 17142 link(entry.n, entry.p); 17143 link(entry, freshEnd); 17144 freshEnd = entry; 17145 freshEnd.n = null; 17146 } 17147 } 17148 17149 17150 /** 17151 * bidirectionally links two entries of the LRU linked list 17152 */ 17153 function link(nextEntry, prevEntry) { 17154 if (nextEntry !== prevEntry) { 17155 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify 17156 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify 17157 } 17158 } 17159 } 17160 17161 17162 /** 17163 * @ngdoc method 17164 * @name $cacheFactory#info 17165 * 17166 * @description 17167 * Get information about all the caches that have been created 17168 * 17169 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` 17170 */ 17171 cacheFactory.info = function() { 17172 var info = {}; 17173 forEach(caches, function(cache, cacheId) { 17174 info[cacheId] = cache.info(); 17175 }); 17176 return info; 17177 }; 17178 17179 17180 /** 17181 * @ngdoc method 17182 * @name $cacheFactory#get 17183 * 17184 * @description 17185 * Get access to a cache object by the `cacheId` used when it was created. 17186 * 17187 * @param {string} cacheId Name or id of a cache to access. 17188 * @returns {object} Cache object identified by the cacheId or undefined if no such cache. 17189 */ 17190 cacheFactory.get = function(cacheId) { 17191 return caches[cacheId]; 17192 }; 17193 17194 17195 return cacheFactory; 17196 }; 17197} 17198 17199/** 17200 * @ngdoc service 17201 * @name $templateCache 17202 * @this 17203 * 17204 * @description 17205 * The first time a template is used, it is loaded in the template cache for quick retrieval. You 17206 * can load templates directly into the cache in a `script` tag, or by consuming the 17207 * `$templateCache` service directly. 17208 * 17209 * Adding via the `script` tag: 17210 * 17211 * ```html 17212 * <script type="text/ng-template" id="templateId.html"> 17213 * <p>This is the content of the template</p> 17214 * </script> 17215 * ``` 17216 * 17217 * **Note:** the `script` tag containing the template does not need to be included in the `head` of 17218 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, 17219 * element with ng-app attribute), otherwise the template will be ignored. 17220 * 17221 * Adding via the `$templateCache` service: 17222 * 17223 * ```js 17224 * var myApp = angular.module('myApp', []); 17225 * myApp.run(function($templateCache) { 17226 * $templateCache.put('templateId.html', 'This is the content of the template'); 17227 * }); 17228 * ``` 17229 * 17230 * To retrieve the template later, simply use it in your component: 17231 * ```js 17232 * myApp.component('myComponent', { 17233 * templateUrl: 'templateId.html' 17234 * }); 17235 * ``` 17236 * 17237 * or get it via the `$templateCache` service: 17238 * ```js 17239 * $templateCache.get('templateId.html') 17240 * ``` 17241 * 17242 * See {@link ng.$cacheFactory $cacheFactory}.
vendor: 5,589 bytes, lines 17242-17371
17242 17243 * 17244 */ 17245function $TemplateCacheProvider() { 17246 this.$get = ['$cacheFactory', function($cacheFactory) { 17247 return $cacheFactory('templates'); 17248 }]; 17249} 17250 17251/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 17252 * Any commits to this file should be reviewed with security in mind. * 17253 * Changes to this file can potentially create security vulnerabilities. * 17254 * An approval from 2 Core members with history of modifying * 17255 * this file is required. * 17256 * * 17257 * Does the change somehow allow for arbitrary javascript to be executed? * 17258 * Or allows for someone to change the prototype of built-in objects? * 17259 * Or gives undesired access to variables like document or window? * 17260 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 17261 17262/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! 17263 * 17264 * DOM-related variables: 17265 * 17266 * - "node" - DOM Node 17267 * - "element" - DOM Element or Node 17268 * - "$node" or "$element" - jqLite-wrapped node or element 17269 * 17270 * 17271 * Compiler related stuff: 17272 * 17273 * - "linkFn" - linking fn of a single directive 17274 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node 17275 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node 17276 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) 17277 */ 17278 17279 17280/** 17281 * @ngdoc service 17282 * @name $compile 17283 * @kind function 17284 * 17285 * @description 17286 * Compiles an HTML string or DOM into a template and produces a template function, which 17287 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. 17288 * 17289 * The compilation is a process of walking the DOM tree and matching DOM elements to 17290 * {@link ng.$compileProvider#directive directives}. 17291 * 17292 * <div class="alert alert-warning"> 17293 * **Note:** This document is an in-depth reference of all directive options. 17294 * For a gentle introduction to directives with examples of common use cases, 17295 * see the {@link guide/directive directive guide}. 17296 * </div> 17297 * 17298 * ## Comprehensive Directive API 17299 * 17300 * There are many different options for a directive. 17301 * 17302 * The difference resides in the return value of the factory function. 17303 * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)} 17304 * that defines the directive properties, or just the `postLink` function (all other properties will have 17305 * the default values). 17306 * 17307 * <div class="alert alert-success"> 17308 * **Best Practice:** It's recommended to use the "directive definition object" form. 17309 * </div> 17310 * 17311 * Here's an example directive declared with a Directive Definition Object: 17312 * 17313 * ```js 17314 * var myModule = angular.module(...); 17315 * 17316 * myModule.directive('directiveName', function factory(injectables) { 17317 * var directiveDefinitionObject = { 17318 * {@link $compile#-priority- priority}: 0, 17319 * {@link $compile#-template- template}: '<div></div>', // or // function(tElement, tAttrs) { ... }, 17320 * // or 17321 * // {@link $compile#-templateurl- templateUrl}: 'directive.html', // or // function(tElement, tAttrs) { ... }, 17322 * {@link $compile#-transclude- transclude}: false, 17323 * {@link $compile#-restrict- restrict}: 'A', 17324 * {@link $compile#-templatenamespace- templateNamespace}: 'html', 17325 * {@link $compile#-scope- scope}: false, 17326 * {@link $compile#-controller- controller}: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, 17327 * {@link $compile#-controlleras- controllerAs}: 'stringIdentifier', 17328 * {@link $compile#-bindtocontroller- bindToController}: false, 17329 * {@link $compile#-require- require}: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], 17330 * {@link $compile#-multielement- multiElement}: false, 17331 * {@link $compile#-compile- compile}: function compile(tElement, tAttrs, transclude) { 17332 * return { 17333 * {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... }, 17334 * {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... } 17335 * } 17336 * // or 17337 * // return function postLink( ... ) { ... } 17338 * }, 17339 * // or 17340 * // {@link $compile#-link- link}: { 17341 * // {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... }, 17342 * // {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... } 17343 * // } 17344 * // or 17345 * // {@link $compile#-link- link}: function postLink( ... ) { ... } 17346 * }; 17347 * return directiveDefinitionObject; 17348 * }); 17349 * ``` 17350 * 17351 * <div class="alert alert-warning"> 17352 * **Note:** Any unspecified options will use the default value. You can see the default values below. 17353 * </div> 17354 * 17355 * Therefore the above can be simplified as: 17356 * 17357 * ```js 17358 * var myModule = angular.module(...); 17359 * 17360 * myModule.directive('directiveName', function factory(injectables) { 17361 * var directiveDefinitionObject = { 17362 * link: function postLink(scope, iElement, iAttrs) { ... } 17363 * }; 17364 * return directiveDefinitionObject; 17365 * // or 17366 * // return function postLink(scope, iElement, iAttrs) { ... } 17367 * }); 17368 * ``` 17369 * 17370 * ### Life-cycle hooks 17371 * Directive controllers can provide the following methods that are called by Angular
17371 at points in the life-cycle of the 17372 * directive: 17373 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and 17374 * had their bindings initialized (and before the pre & post linking functions for the directives on 17375 * this element). This is a good place to put initialization code for your controller. 17376 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The 17377 * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an 17378 * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a 17379 * component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will 17380 * also be called when your bindings are initialized. 17381 * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on 17382 * changes. Any actions that you wish to take in response to the changes that you detect must be 17383 * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook 17384 * could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which w
17384ould not 17385 * be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments; 17386 * if detecting changes, you must store the previous value(s) for comparison to the current values. 17387 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing 17388 * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in 17389 * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent 17390 * components will have their `$onDestroy()` hook called before child components. 17391 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link 17392 * function this hook can be used to set up DOM event handlers and do direct DOM manipulation. 17393 * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since 17394 * they are waiting for their template to load asynchronously and their own compilation and linking has been 17395 * suspended until that occurs. 17396 * 17397 * #### Comparison with Angular 2 life-cycle hooks 17398 * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are 17399 * some differences that you should be aware of, especially when it comes to moving your co
17399de from Angular 1 to Angular 2: 17400 * 17401 * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`. 17402 * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor. 17403 * In Angular 2 you can only define hooks on the prototype of the Component class. 17404 * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to 17405 * `ngDoCheck` in Angular 2 17406 * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be 17407 * propagated throughout the application. 17408 * Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an 17409 * error or do nothing depending upon the state of `enableProdMode()`. 17410 * 17411 * #### Life-cycle hook examples 17412 * 17413 * This example shows how you can check for mutations to a Date object even though the identity of the object 17414 * has not changed. 17415 * 17416 * <example name="doCheckDateExample" module="do-check-module"> 17417 * <file name="app.js"> 17418 * angular.module('do-check-module', []) 17419 * .component('app', { 17420 * template: 17421 * 'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' + 17422 * 'Date: {{ $ctrl.date }}' + 17423 * '<test date="$ctrl.date"></test>', 17424 * controller: function() { 17425 * this.date = new Date(); 17426 * this.month = this.date.getMonth(); 17427 * this.updateDate = function() { 17428 * this.date.setMonth(this.month); 17429 * }; 17430 * } 17431 * }) 17432 * .component('test', { 17433 * bindings: { date: '<' }, 17434 * template: 17435 * '<pre>{{ $ctrl.log | json }}</pre>', 17436 * controller: function() { 17437 * var previousValue; 17438 * this.log = []; 17439 * this.$doCheck = function() { 17440 * var currentValue = this.date && this.date.valueOf(); 17441 * if (previousValue !== currentValue) { 17442 * this.log.push('doCheck: date mutated: ' + this.date); 17443 * previousValue = currentValue; 17444 * } 17445 * }; 17446 * } 17447 * }); 17448 * </file> 17449 * <file name="index.html"> 17450 * <app></app> 17451 * </file> 17452 * </example> 17453 * 17454 * This example show how you might use `$doCheck` to trigger changes in your component's input
17454s even if the 17455 * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large 17456 * arrays or objects can have a negative impact on your application performance) 17457 * 17458 * <example name="doCheckArrayExample" module="do-check-module"> 17459 * <file name="index.html"> 17460 * <div ng-init="items = []"> 17461 * <button ng-click="items.push(items.length)">Add Item</button> 17462 * <button ng-click="items = []">Reset Items</button> 17463 * <pre>{{ items }}</pre> 17464 * <test items="items"></test> 17465 * </div> 17466 * </file> 17467 * <file name="app.js"> 17468 * angular.module('do-check-module', []) 17469 * .component('test', { 17470 * bindings: { items: '<' }, 17471 * template: 17472 * '<pre>{{ $ctrl.log | json }}</pre>', 17473 * controller: function() { 17474 * this.log = []; 17475 * 17476 * this.$doCheck = function() { 17477 * if (this.items_ref !== this.items) { 17478 * this.log.push('doCheck: items changed'); 17479 * this.items_ref = this.items; 17480 * } 17481 * if (!angular.equals(this.items_clone, this.items)) { 17482 * this.log.push('doCheck: items mutated'); 17483 * this.items_clone = angular.copy(this.items); 17484 * } 17485 * }; 17486 * } 17487 * }); 17488 * </file> 17489 * </example> 17490 * 17491 * 17492 * ### Directive Definition Object 17493 * 17494 * The directive definition object provides instructions to the {@link ng.$compile 17495 * compiler}. The attributes are: 17496 * 17497 * #### `multiElement` 17498 * When this property is set to true (default is `false`), the HTML compiler will collect DOM nodes between 17499 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them 17500 * together as the directive elements. It is recommended that this feature be used on directives 17501 * which are not strictly behavioral (such as {@link ngClick}), and which 17502 * do not manipulate or replace child nodes (such as {@link ngInclude}). 17503 * 17504 * #### `priority` 17505 * When there are multiple directives defined on a single DOM element, sometimes it 17506 * is necessary to specify the order in which the directives are applied. The `priority` is used 17507 * to sort the directives before their `compile` functions get called. Priority is defined as a 17508 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions 17509 * are also run in priority order, but post-link functions are run in reverse order. The order 17510 * of directives with the same priority is undefined. The default priority is `0`. 17511 * 17512 * #### `terminal` 17513 * If set to true then the current `priority` will be the last set of directives 17514 * which will execute (any directives at the current priority will still execute 17515 * as the order of execution on same `priority` is undefined). Note that expressions 17516 * and other directives used in the directive's template will also be excluded from execution. 17517 * 17518 * #### `scope` 17519 * The scope property can be `false`, `true`, or an object: 17520 * 17521 * * **`false` (default):** No scope will be created for the directive. The directive will use its 17522 * parent's scope. 17523 * 17524 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for 17525 * the directive's element. If multiple directives on the same element request a new scope, 17526 * only one new scope is created. 17527 * 17528 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template. 17529 * The 'isolate' scope differs from normal scope in that it does not prototypically 17530 * inherit from its parent scope. This is useful when creating reusable components, which should not 17531 * accidentally read or modify data in the parent scope. Note that an isolate scope 17532 * directive without a `template` or `templateUrl` will not apply the isolate scope 17533 * to its children elements. 17534 * 17535 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the 17536 * directive's element. These local properties are useful for aliasing values for templates. The keys in 17537 * the object hash map to the name of the property on the isolate scope; the values define how the property 17538 * is bound to the parent scope, via matching attributes on the directive's element: 17539 * 17540 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
17541 * always a string since DOM attributes are strings. If no `attr` name is specified then the 17542 * attribute name is assumed to be the same as the local name. Given `<my-component 17543 * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`, 17544 * the directive's scope property `localName` will reflect the interpolated value of `hello 17545 * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's 17546 * scope. The `name` is read from the parent scope (not the directive's scope). 17547 * 17548 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression 17549 * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope. 17550 * If no `attr` name is specified then the attribute name is assumed to be the same as the local 17551 * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: { 17552 * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the 17553 * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in 17554 * `localModel` and vice versa. Optional attributes should be marked as such with a question mark: 17555 * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't 17556 * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`}) 17557 * will be thrown upon discovering changes to the local value, since it will be impossible to sync 17558 * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`} 17559 * method is used for tracking changes, and the equality check is based on object identity. 17560 * However, if an object literal or an array literal is passed as the binding expression, the 17561 * equality check is done by value (using the {@link angular.equals} function). It's also possible 17562 * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection 17563 * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional). 17564 * 17565 * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an 17566 * expression passed via the attribute `attr`. The expression is evaluated in the context of the 17567 * parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the 17568 * local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`. 17569 * 17570 * For example, given `<my-component my-attr="parentModel">` and directive definition of 17571 * `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the 17572 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected 17573 * in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however 17574 * two caveats: 17575 * 1. one-way binding does not copy the value from the parent to the isolate scope, it simply 17576 * sets the same value. That means if your bound value is an object, changes to its properties 17577 * in the isolated scope will be reflected in the parent scope (because both reference the same object). 17578 * 2. one-way binding watches changes to the **identity** of the parent value. That means the 17579 * {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference 17580 * to the value has changed. In most cases, this should not be of concern, but can be important 17581 * to know if you one-way bind to an object, and then replace that object in the isolated scope. 17582 * If you now change a property of the object in your parent scope, the change will not be 17583 * propagated to the isolated scope, because the identity of the object on the parent scope 17584 * has not changed. Instead you must assign a new object. 17585 * 17586 * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings 17587 * back to the parent. However, it does not make this completely impossible. 17588 * 17589 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If 17590 * no `attr` name is specified then the attribute name is assumed to be the same as the local name. 17591 * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: { 17592 * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for 17593 * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope 17594 * via an expression to the parent scope. This can be done by passing a map of local variable names 17595 * and values into the expression wrapper fn. For example, if the expression is `increment(amount)` 17596 * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`. 17597 * 17598 * In general it's possible to apply more than one directive to one element, but there might be limitations 17599 * depending on the type of scope required by the directives. The following points will help explain these limitations. 17600 * For simplicity only two directives are taken into account, but it is also applicable for several directives: 17601 * 17602 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope 17603 * * **child scope** + **no scope** => Both directives will share one single child scope 17604 * * **child scope** + **child scope** => Both directives will share one single child scope 17605 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use 17606 * its parent's scope 17607 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot 17608 * be applied to the same element. 17609 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives 17610 * cannot be applied to the same element. 17611 * 17612 * 17613 * #### `bindToController` 17614 * This property is used to bind scope properties directly to the controller. It can be either 17615 * `true` or an object hash with the same format as the `scope` property. 17616 * 17617 * When an isolate scope is used for a directive (see above), `bindToController: true` will 17618 * allow a component to have its properties bound to the controller, rather than to scope. 17619 * 17620 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller 17621 * properties. You can access these bindings once they have been initialized by providing a controller method called 17622 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings 17623 * initialized. 17624 * 17625 * <div class="alert alert-warning"> 17626 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
17627 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization 17628 * code that relies upon bindings inside a `$onInit` method on the controller, instead. 17629 * </div> 17630 * 17631 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property. 17632 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used 17633 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate 17634 * scope (useful for component directives). 17635 * 17636 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`. 17637 * 17638 * 17639 * #### `controller` 17640 * Controller constructor function. The controller is instantiated before the 17641 * pre-linking phase and can be accessed by other directives (see 17642 * `require` attribute). This allows the directives to communicate with each other and augment 17643 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: 17644 * 17645 * * `$scope` - Current scope associated with the element 17646 * * `$element` - Current element 17647 * * `$attrs` - Current attributes object for the element 17648 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: 17649 * `function([scope], cloneLinkingFn, futureParentElement, slotName)`: 17650 * * `scope`: (optional) override the scope. 17651 * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content. 17652 * * `futureParentElement` (optional): 17653 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements. 17654 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`. 17655 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements) 17656 * and when the `cloneLinkingFn` is passed, 17657 * as those elements need to created and cloned in a special way when they are defined outside their 17658 * usual containers (e.g. like `<svg>`). 17659 * * See also the `directive.templateNamespace` property. 17660 * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`) 17661 * then the default transclusion is provided. 17662 * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns 17663 * `true` if the specified slot contains content (i.e. one or more DOM nodes). 17664 * 17665 * #### `require` 17666 * Require another directive and inject its controller as the fourth argument to the linking function. The 17667 * `require` property can be a string, an array or an object: 17668 * * a **string** containing the name of the directive to pass to the linking function 17669 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the 17670 * linking function will be an array of controllers in the same order as the names in the `require` property 17671 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument 17672 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding 17673 * controllers. 17674 * 17675 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are 17676 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers 17677 * have been constructed but before `$onInit` is called. 17678 * If the name of the required controller is the same as the local name (the key), the name can be 17679 * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`. 17680 * See the {@link $compileProvider#component} helper for an example of how this can be used. 17681 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is 17682 * raised (unless no link function is specified and the required controllers are not being bound to the directive 17683 * controller, in which case error checking is skipped). The name can be prefixed with: 17684 * 17685 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. 17686 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. 17687 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. 17688 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found. 17689 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass 17690 * `null` to the `link` fn if not found. 17691 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass 17692 * `null` to the `link` fn if not found. 17693 * 17694 * 17695 * #### `controllerAs` 17696 * Identifier name for a reference to the controller in the directive's scope. 17697 * This allows the controller to be referenced from the directive template. This is especially 17698 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible 17699 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the 17700 * `controllerAs` reference might overwrite a property that already exists on the parent scope. 17701 * 17702 * 17703 * #### `restrict` 17704 * String of subset of `EACM` which restricts the directive to a specific directive 17705 * declaration style. If omitted, the defaults (elements and attributes) are used. 17706 * 17707 * * `E` - Element name (default): `<my-directive></my-directive>` 17708 * * `A` - Attribute (default): `<div my-directive="exp"></div>` 17709 * * `C` - Class: `<div class="my-directive: exp;"></div>` 17710 * * `M` - Comment: `<!-- directive: my-directive exp -->` 17711 * 17712 * 17713 * #### `templateNamespace` 17714 * String representing the document type used by the markup in the template. 17715 * AngularJS needs this information as those elements need to be created and cloned 17716 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`. 17717 * 17718 * * `html` - All root nodes in the template are HTML. Root nodes may also be 17719 * top-level elements such as `<svg>` or `<math>`. 17720 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`). 17721 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`). 17722 * 17723 * If no `templateNamespace` is specified, then the namespace is considered to be `html`. 17724 * 17725 * #### `template` 17726 * HTML markup that may: 17727 * * Replace the contents of the directive's element (default). 17728 * * Replace the directive's element itself (if `replace` is true - DEPRECATED). 17729 * * Wrap the contents of the directive's element (if `transclude` is true). 17730 * 17731 * Value may be: 17732 * 17733 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`. 17734 * * A function which takes two arguments `tElement` and `tAttrs` (described
17734in the `compile` 17735 * function api below) and returns a string value. 17736 * 17737 * 17738 * #### `templateUrl` 17739 * This is similar to `template` but the template is loaded from the specified URL, asynchronously. 17740 * 17741 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element 17742 * for later when the template has been resolved. In the meantime it will continue to compile and link 17743 * sibling and parent elements as though this element had not contained any directives. 17744 * 17745 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this 17746 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the 17747 * case when only one deeply nested directive has `templateUrl`. 17748 * 17749 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} 17750 * 17751 * You can specify `templateUrl` as a string representing the URL or as a function which takes two 17752 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns 17753 * a string value representing the url. In either case, the template URL is passed through {@link 17754 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. 17755 * 17756 * 17757 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) 17758 * specify what the template should replace. Defaults to `false`. 17759 * 17760 * * `true` - the template will replace the directive's element. 17761 * * `false` - the template will replace the contents of the directive's element. 17762 * 17763 * The replacement process migrates all of the attributes / classes from the old element to the new 17764 * one. See the {@link guide/directive#template-expanding-directive 17765 * Directives Guide} for an example. 17766 * 17767 * There are very few scenarios where element replacement is required for the application function, 17768 * the main one being reusable custom components that are used within SVG contexts 17769 * (because SVG doesn't work with custom elements in the DOM tree). 17770 * 17771 * #### `transclude` 17772 * Extract the contents of the element where the directive appears and make it available to the directive. 17773 * The contents are compiled and provided to the directive as a **transclusion function**. See the 17774 * {@link $compile#transclusion Transclusion} section below. 17775 * 17776 * 17777 * #### `compile` 17778 * 17779 * ```js 17780 * function compile(tElement, tAttrs, transclude) { ... } 17781 * ``` 17782 * 17783 * The compile function deals with transforming the template DOM. Since most directives do not do 17784 * template transformation, it is not used often. The compile function takes the following arguments: 17785 * 17786 * * `tElement` - template element - The element where the directive has been declared. It is 17787 * safe to do template transformation on the element and child elements only. 17788 * 17789 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared 17790 * between all directive compile functions. 17791 * 17792 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` 17793 * 17794 * <div class="alert alert-warning"> 17795 * **Note:** The template instance and the link instance may be different objects if the template has 17796 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that 17797 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration 17798 * should be done in a linking function rather than in a compile function. 17799 * </div> 17800 17801 * <div class="alert alert-warning"> 17802 * **Note:** The compile function cannot handle directives that recursively use themselves in their 17803 * own templates or compile functions. Compiling these directives results in an infinite loop and 17804 * stack overflow errors. 17805 * 17806 * This can be avoided by manually using $compile
vendor: 5,458 bytes, lines 17806-17910
17806 in the postLink function to imperatively compile 17807 * a directive's template instead of relying on automatic template compilation via `template` or 17808 * `templateUrl` declaration or manual compilation inside the compile function. 17809 * </div> 17810 * 17811 * <div class="alert alert-danger"> 17812 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it 17813 * e.g. does not know about the right outer scope. Please use the transclude function that is passed 17814 * to the link function instead. 17815 * </div> 17816 17817 * A compile function can have a return value which can be either a function or an object. 17818 * 17819 * * returning a (post-link) function - is equivalent to registering the linking function via the 17820 * `link` property of the config object when the compile function is empty. 17821 * 17822 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to 17823 * control when a linking function should be called during the linking phase. See info about 17824 * pre-linking and post-linking functions below. 17825 * 17826 * 17827 * #### `link` 17828 * This property is used only if the `compile` property is not defined. 17829 * 17830 * ```js 17831 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } 17832 * ``` 17833 * 17834 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is 17835 * executed after the template has been cloned. This is where most of the directive logic will be 17836 * put. 17837 * 17838 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the 17839 * directive for registering {@link ng.$rootScope.Scope#$watch watches}. 17840 * 17841 * * `iElement` - instance element - The element where the directive is to be used. It is safe to 17842 * manipulate the children of the element only in `postLink` function since the children have 17843 * already been linked. 17844 * 17845 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared 17846 * between all directive linking functions. 17847 * 17848 * * `controller` - the directive's required controller instance(s) - Instances are shared 17849 * among all directives, which allows the directives to use the controllers as a communication 17850 * channel. The exact value depends on the directive's `require` property: 17851 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one 17852 * * `string`: the controller instance 17853 * * `array`: array of controller instances 17854 * 17855 * If a required controller cannot be found, and it is optional, the instance is `null`, 17856 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown. 17857 * 17858 * Note that you can also require the directive's own controller - it will be made available like 17859 * any other controller. 17860 * 17861 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. 17862 * This is the same as the `$transclude` parameter of directive controllers, 17863 * see {@link ng.$compile#-controller- the controller section for details}. 17864 * `function([scope], cloneLinkingFn, futureParentElement)`. 17865 * 17866 * #### Pre-linking function 17867 * 17868 * Executed before the child elements are linked. Not safe to do DOM transformation since the 17869 * compiler linking function will fail to locate the correct elements for linking. 17870 * 17871 * #### Post-linking function 17872 * 17873 * Executed after the child elements are linked. 17874 * 17875 * Note that child elements that contain `templateUrl` directives will not have been compiled 17876 * and linked since they are waiting for their template to load asynchronously and their own 17877 * compilation and linking has been suspended until that occurs. 17878 * 17879 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting 17880 * for their async templates to be resolved. 17881 * 17882 * 17883 * ### Transclusion 17884 * 17885 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and 17886 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS 17887 * scope from where they were taken. 17888 * 17889 * Transclusion is used (often with {@link ngTransclude}) to insert the 17890 * original contents of a directive's element into a specified place in the template of the directive. 17891 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded 17892 * content has access to the properties on the scope from which it was taken, even if the directive 17893 * has isolated scope. 17894 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}. 17895 * 17896 * This makes it possible for the widget to have private state for its template, while the transcluded 17897 * content has access to its originating scope. 17898 * 17899 * <div class="alert alert-warning"> 17900 * **Note:** When testing an element transclude directive you must not place the directive at the root of the 17901 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives 17902 * Testing Transclusion Directives}. 17903 * </div> 17904 * 17905 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the 17906 * directive's element, the entire element or multiple parts of the element contents: 17907 * 17908 * * `true` - transclude the content (i.e. the child nodes) of the directive's element. 17909 * * `'element'` - transclude the whole of the directive's element including any directives on this 17910 * element that
17910defined at a lower priority than this directive. When used, the `template` 17911 * property is ignored. 17912 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template. 17913 * 17914 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property. 17915 * 17916 * This object is a map where the keys are the name of the slot to fill and the value is an element selector 17917 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`) 17918 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc). 17919 * 17920 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} 17921 * 17922 * If the element selector is prefixed with a `?` then that slot is optional. 17923 * 17924 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to 17925 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive. 17926 * 17927 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements 17928 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call 17929 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and 17930 * injectable into the directive's controller. 17931 * 17932 * 17933 * #### Transclusion Functions 17934 * 17935 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion 17936 * function** to the directive's `link` function and `controller`. This transclusion function is a special 17937 * **linking function** that will return the compiled contents linked to a new transclusion scope. 17938 * 17939 * <div class="alert alert-info"> 17940 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since 17941 * ngTransclude will deal with it for us. 17942 * </div> 17943 * 17944 * If you want to manually control the insertion and removal of the transcluded content in your directive 17945 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery 17946 * object that contains the compiled DOM, which is linked to the correct transclusion scope. 17947 * 17948 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts 17949 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded 17950 * content and the `scope` is the newly created transclusion scope, which the clone will be linked to. 17951 * 17952 * <div class="alert alert-info"> 17953 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function 17954 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. 17955 * </div> 17956 * 17957 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone 17958 * attach function**: 17959 * 17960 * ```js 17961 * var transcludedContent, transclusionScope; 17962 * 17963 * $transclude(function(clone, scope) { 17964 * element.append(clone); 17965 * transcludedContent = clone; 17966 * transclusionScope = scope; 17967 * }); 17968 * ``` 17969 * 17970 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the 17971 * associated transclusion scope: 17972 * 17973 * ```js 17974 * transcludedContent.remove(); 17975 * transclusionScope.$destroy(); 17976 * ``` 17977 * 17978 * <div class="alert alert-info"> 17979 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive 17980 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it), 17981 * then you are also responsible for calling `$destroy` on the transclusion scope. 17982 * </div> 17983 * 17984 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat} 17985 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if 17986 * you are simply using {@link ngTransclude} to inject the transclusion into your directive. 17987 * 17988 * 17989 * #### Transclusion Scopes 17990 * 17991 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion 17992 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed 17993 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it 17994 * was taken. 17995 * 17996 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look 17997 * like this: 17998 * 17999 * ```html 18000 * <div ng-app> 18001 * <div isolate> 18002 * <div transclusion>
18003 * </div> 18004 * </div> 18005 * </div> 18006 * ``` 18007 * 18008 * The `$parent` scope hierarchy will look like this: 18009 * 18010 ``` 18011 - $rootScope 18012 - isolate 18013 - transclusion 18014 ``` 18015 * 18016 * but the scopes will inherit prototypically from different scopes to their `$parent`. 18017 * 18018 ``` 18019 - $rootScope 18020 - transclusion 18021 - isolate 18022 ``` 18023 * 18024 * 18025 * ### Attributes 18026 * 18027 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the 18028 * `link()` or `compile()` functions. It has a variety of uses. 18029 * 18030 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways: 18031 * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access 18032 * to the attributes. 18033 * 18034 * * *Directive inter-communication:* All directives share the same instance of the attributes 18035 * object which allows the directives to use the attributes object as inter directive 18036 * communication. 18037 * 18038 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object 18039 * allowing other directives to read the interpolated value. 18040 * 18041 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes 18042 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also 18043 * the only way to easily get the actual value because during the linking phase the interpolation 18044 * hasn't been evaluated yet and so the value is at this time set to `undefined`. 18045 * 18046 * ```js 18047 * function linkingFn(scope, elm, attrs, ctrl) { 18048 * // get the attribute value 18049 * console.log(attrs.ngModel); 18050 * 18051 * // change the attribute 18052 * attrs.$set('ngModel', 'new value'); 18053 * 18054 * // observe changes to interpolated attribute 18055 * attrs.$observe('ngModel', function(value) { 18056 * console.log('ngModel has changed value to ' + value); 18057 * }); 18058 * } 18059 * ``` 18060 * 18061 * ## Example 18062 * 18063 * <div class="alert alert-warning"> 18064 * **Note**: Typically directives are registered with `module.directive`. The example below is 18065 * to illustrate how `$compile` works. 18066 * </div> 18067 * 18068 <example module="compileExample" name="compile"> 18069 <file name="index.html"> 18070 <script> 18071 angular.module('compileExample', [], function($compileProvider) { 18072 // configure new 'compile' directive by passing a directive 18073 // factory function. The factory function injects the '$compile' 18074 $compileProvider.directive('compile', function($compile) { 18075 // directive factory creates a link function 18076 return function(scope, element, attrs) { 18077 scope.$watch( 18078 function(scope) { 18079 // watch the 'compile' expression for changes 18080 return scope.$eval(attrs.compile); 18081 }, 18082 function(value) { 18083 // when the 'compile' expression changes 18084 // assign it into the current DOM 18085 element.html(value); 18086 18087 // compile the new DOM and link it to the current 18088 // scope. 18089 // NOTE: we only compile .childNodes so that 18090 // we don't get into infinite loop compiling ourselves 18091 $compile(element.contents())(scope); 18092 } 18093 ); 18094 }; 18095 }); 18096 }) 18097 .controller('GreeterController', ['$scope', function($scope) { 18098 $scope.name = 'Angular'; 18099 $scope.html = 'Hello {{name}}'; 18100 }]); 18101 </script> 18102 <div ng-controller="GreeterController"> 18103 <input ng-model="name"> <br/> 18104 <textarea ng-model="html"></textarea> <br/> 18105 <div compile="html"></div> 18106 </div> 18107 </file> 18108 <file name="protractor.js" type="protractor"> 18109 it('should auto compile', function() { 18110 var textarea = $('textarea'); 18111 var output = $('div[compile]'); 18112 // The initial state reads 'Hello Angular'. 18113 expect(output.getText()).toBe('Hello Angular'); 18114 textarea.clear(); 18115 textarea.sendKeys('{{name}}!'); 18116 expect(output.getText()).toBe('Angular!'); 18117 }); 18118 </file> 18119 </example> 18120 18121 * 18122 * 18123 * @param {string|DOMElement} element Element or HTML string to compile into a template function. 18124 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. 18125 * 18126 * <div class="alert alert-danger"> 18127 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it 18128 * e.g. will not use the right outer scope. Please pass the transclude function as a 18129 * `parentBoundTranscludeFn` to the link function instead. 18130 * </div> 18131 * 18132 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
18133 * root element(s), not their children) 18134 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template 18135 * (a DOM element/tree) to a scope. Where: 18136 * 18137 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. 18138 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the 18139 * `template` and call the `cloneAttachFn` function allowing the caller to attach the 18140 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is 18141 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where: 18142 * 18143 * * `clonedElement` - is a clone of the original `element` passed into the compiler. 18144 * * `scope` - is the current scope with which the linking function is working with. 18145 * 18146 * * `options` - An optional object hash with linking options. If `options` is provided, then the following 18147 * keys may be used to control linking behavior: 18148 * 18149 * * `parentBoundTranscludeFn` - the transclude function made available to 18150 * directives; if given, it will be passed through to the link functions of 18151 * directives found in `element` during compilation. 18152 * * `transcludeControllers` - an object hash with keys that map controller names 18153 * to a hash with the key `instance`, which maps to the controller instance; 18154 * if given, it will make the controllers available to directives on the compileNode: 18155 * ``` 18156 * { 18157 * parent: { 18158 * instance: parentControllerInstance 18159 * } 18160 * } 18161 * ``` 18162 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add 18163 * the cloned elements; only needed for transcludes that are allowed to contain non html 18164 * elements (e.g. SVG elements). See also the directive.controller property. 18165 * 18166 * Calling the linking function returns the element of the template. It is either the original 18167 * element passed in, or the clone of the element if the `cloneAttachFn` is provided. 18168 * 18169 * After linking the view is not updated until after a call to $digest which typically is done by 18170 * Angular automatically. 18171 * 18172 * If you need access to the bound view, there are two ways to do it: 18173 * 18174 * - If you are not asking the linking function to clone the template, create the DOM element(s) 18175 * before you send them to the compiler and keep this reference around. 18176 * ```js 18177 * var element = $compile('<p>{{total}}</p>')(scope); 18178 * ``` 18179 * 18180 * - if on the other hand, you need the element to be cloned, the view reference from the original 18181 * example would not point to the clone, but rather to the original template that was cloned. In 18182 * this case, you can access the clone via the cloneAttachFn: 18183 * ```js 18184 * var templateElement = angular.element('<p>{{total}}</p>'), 18185 * scope = ....; 18186 * 18187 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { 18188 * //attach the clone to DOM document at the right place 18189 * }); 18190 * 18191 * //now we have reference to the cloned DOM via `clonedElement` 18192 * ``` 18193 * 18194 * 18195 * For information on how the compiler works, see the 18196 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. 18197 * 18198 * @knownIssue 18199 * 18200 * ### Double Compilation 18201 * 18202 Double compilation occurs when an already compiled part of the DOM gets 18203 compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues, 18204 and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it 18205 section on double compilation} for an in-depth explanation and ways to avoid it. 18206 * 18207 */ 18208 18209var $compileMinErr = minErr('$compile'); 18210 18211function UNINITIALIZED_VALUE() {} 18212var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE(); 18213 18214/** 18215 * @ngdoc provider 18216 * @name $compileProvider 18217 * 18218 * @description 18219 */
vendor: 15,193 bytes, lines 18220-18587
18220$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; 18221/** @this */ 18222function $CompileProvider($provide, $$sanitizeUriProvider) { 18223 var hasDirectives = {}, 18224 Suffix = 'Directive', 18225 COMMENT_DIRECTIVE_REGEXP = /^\s*directive:\s*([\w-]+)\s+(.*)$/, 18226 CLASS_DIRECTIVE_REGEXP = /(([\w-]+)(?::([^;]+))?;?)/, 18227 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), 18228 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; 18229 18230 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes 18231 // The assumption is that future DOM event attribute names will begin with 18232 // 'on' and be composed of only English letters. 18233 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; 18234 var bindingCache = createMap(); 18235 18236 function parseIsolateBindings(scope, directiveName, isController) { 18237 var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/; 18238 18239 var bindings = createMap(); 18240 18241 forEach(scope, function(definition, scopeName) { 18242 if (definition in bindingCache) { 18243 bindings[scopeName] = bindingCache[definition]; 18244 return; 18245 } 18246 var match = definition.match(LOCAL_REGEXP); 18247 18248 if (!match) { 18249 throw $compileMinErr('iscp', 18250 'Invalid {3} for directive \'{0}\'.' + 18251 ' Definition: {... {1}: \'{2}\' ...}', 18252 directiveName, scopeName, definition, 18253 (isController ? 'controller bindings definition' : 18254 'isolate scope definition')); 18255 } 18256 18257 bindings[scopeName] = { 18258 mode: match[1][0], 18259 collection: match[2] === '*', 18260 optional: match[3] === '?', 18261 attrName: match[4] || scopeName 18262 }; 18263 if (match[4]) { 18264 bindingCache[definition] = bindings[scopeName]; 18265 } 18266 }); 18267 18268 return bindings; 18269 } 18270 18271 function parseDirectiveBindings(directive, directiveName) { 18272 var bindings = { 18273 isolateScope: null, 18274 bindToController: null 18275 }; 18276 if (isObject(directive.scope)) { 18277 if (directive.bindToController === true) { 18278 bindings.bindToController = parseIsolateBindings(directive.scope, 18279 directiveName, true); 18280 bindings.isolateScope = {}; 18281 } else { 18282 bindings.isolateScope = parseIsolateBindings(directive.scope, 18283 directiveName, false); 18284 } 18285 } 18286 if (isObject(directive.bindToController)) { 18287 bindings.bindToController = 18288 parseIsolateBindings(directive.bindToController, directiveName, true); 18289 } 18290 if (bindings.bindToController && !directive.controller) { 18291 // There is no controller 18292 throw $compileMinErr('noctrl', 18293 'Cannot bind to controller without directive \'{0}\'s controller.', 18294 directiveName); 18295 } 18296 return bindings; 18297 } 18298 18299 function assertValidDirectiveName(name) { 18300 var letter = name.charAt(0); 18301 if (!letter || letter !== lowercase(letter)) { 18302 throw $compileMinErr('baddir', 'Directive/Component name \'{0}\' is invalid. The first character must be a lowercase letter', name); 18303 } 18304 if (name !== name.trim()) { 18305 throw $compileMinErr('baddir', 18306 'Directive/Component name \'{0}\' is invalid. The name should not contain leading or trailing whitespaces', 18307 name); 18308 } 18309 } 18310 18311 function getDirectiveRequire(directive) { 18312 var require = directive.require || (directive.controller && directive.name); 18313 18314 if (!isArray(require) && isObject(require)) { 18315 forEach(require, function(value, key) { 18316 var match = value.match(REQUIRE_PREFIX_REGEXP); 18317 var name = value.substring(match[0].length); 18318 if (!name) require[key] = match[0] + key; 18319 }); 18320 } 18321 18322 return require; 18323 } 18324 18325 function getDirectiveRestrict(restrict, name) { 18326 if (restrict && !(isString(restrict) && /[EACM]/.test(restrict))) { 18327 throw $compileMinErr('badrestrict', 18328 'Restrict property \'{0}\' of directive \'{1}\' is invalid', 18329 restrict, 18330 name); 18331 } 18332 18333 return restrict || 'EA'; 18334 } 18335 18336 /** 18337 * @ngdoc method 18338 * @name $compileProvider#directive 18339 * @kind function 18340 * 18341 * @description 18342 * Register a new directive with the compiler. 18343 * 18344 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which 18345 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the 18346 * names and the values are the factories. 18347 * @param {Function|Array} directiveFactory An injectable directive factory function. See the 18348 * {@link guide/directive directive guide} and the {@link $compile compile API} for more info. 18349 * @returns {ng.$compileProvider} Self for chaining. 18350 */ 18351 this.directive = function registerDirective(name, directiveFactory) { 18352 assertArg(name, 'name'); 18353 assertNotHasOwnProperty(name, 'directive'); 18354 if (isString(name)) { 18355 assertValidDirectiveName(name); 18356 assertArg(directiveFactory, 'directiveFactory'); 18357 if (!hasDirectives.hasOwnProperty(name)) { 18358 hasDirectives[name] = []; 18359 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', 18360 function($injector, $exceptionHandler) { 18361 var directives = []; 18362 forEach(hasDirectives[name], function(directiveFactory, index) { 18363 try { 18364 var directive = $injector.invoke(directiveFactory); 18365 if (isFunction(directive)) { 18366 directive = { compile: valueFn(directive) }; 18367 } else if (!directive.compile && directive.link) { 18368 directive.compile = valueFn(directive.link); 18369 } 18370 directive.priority = directive.priority || 0; 18371 directive.index = index; 18372 directive.name = directive.name || name; 18373 directive.require = getDirectiveRequire(directive); 18374 directive.restrict = getDirectiveRestrict(directive.restrict, name); 18375 directive.$$moduleName = directiveFactory.$$moduleName; 18376 directives.push(directive); 18377 } catch (e) { 18378 $exceptionHandler(e); 18379 } 18380 }); 18381 return directives; 18382 }]); 18383 } 18384 hasDirectives[name].push(directiveFactory); 18385 } else { 18386 forEach(name, reverseParams(registerDirective)); 18387 } 18388 return this; 18389 }; 18390 18391 /** 18392 * @ngdoc method 18393 * @name $compileProvider#component 18394 * @module ng 18395 * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`) 18396 * @param {Object} options Component definition object (a simplified 18397 * {@link ng.$compile#directive-definition-object directive definition object}), 18398 * with the following properties (all optional): 18399 * 18400 * - `controller` – `{(string|function()=}` – controller constructor function that should be 18401 * associated with newly created scope or the name of a {@link ng.$compile#-controller- 18402 * registered controller} if passed as a string. An empty `noop` function by default. 18403 * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope. 18404 * If present, the controller will be published to scope under the `controllerAs` name. 18405 * If not present, this will default to be `$ctrl`. 18406 * - `template` – `{string=|function()=}` – html template as a string or a function that 18407 * returns an html template as a string which should be used as the contents of this component. 18408 * Empty string by default. 18409 * 18410 * If `template` is a function, then it is {@link auto.$injector#invoke injected} with 18411 * the following locals: 18412 * 18413 * - `$element` - Current element 18414 * - `$attrs` - Current attributes object for the element 18415 * 18416 * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html 18417 * template that should be used as the contents of this component. 18418 * 18419 * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with 18420 * the following locals: 18421 * 18422 * - `$element` - Current element 18423 * - `$attrs` - Current attributes object for the element 18424 * 18425 * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties. 18426 * Component properties are always bound to the component controller and not to the scope. 18427 * See {@link ng.$compile#-bindtocontroller- `bindToController`}. 18428 * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled. 18429 * Disabled by default. 18430 * - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to 18431 * this component's controller. The object keys specify the property names under which the required 18432 * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}. 18433 * - `$...` – additional properties to attach to the directive factory function and the controller 18434 * constructor function. (This is used by the component router to annotate) 18435 * 18436 * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls. 18437 * @description 18438 * Register a **component definition** with the compiler. This is a shorthand for registering a special 18439 * type of directive, which represents a self-contained UI component in your application. Such components 18440 * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`). 18441 * 18442 * Component definitions are very simple and do not require as much configuration as defining general 18443 * directives. Component definitions usually consist only of a template and a controller backing it. 18444 * 18445 * In order to make the definition easier, components enforce best practices like use of `controllerAs`, 18446 * `bindToController`. They always have **isolate scope** and are restricted to elements. 18447 * 18448 * Here are a few examples of how you would usually define components: 18449 * 18450 * ```js 18451 * var myMod = angular.module(...); 18452 * myMod.component('myComp', { 18453 * template: '<div>My name is {{$ctrl.name}}</div>', 18454 * controller: function() { 18455 * this.name = 'shahar'; 18456 * } 18457 * }); 18458 * 18459 * myMod.component('myComp', { 18460 * template: '<div>My name is {{$ctrl.name}}</div>', 18461 * bindings: {name: '@'} 18462 * }); 18463 * 18464 * myMod.component('myComp', { 18465 * templateUrl: 'views/my-comp.html', 18466 * controller: 'MyCtrl', 18467 * controllerAs: 'ctrl', 18468 * bindings: {name: '@'} 18469 * }); 18470 * 18471 * ``` 18472 * For more examples, and an in-depth guide, see the {@link guide/component component guide}. 18473 * 18474 * <br /> 18475 * See also {@link ng.$compileProvider#directive $compileProvider.directive()}. 18476 */ 18477 this.component = function registerComponent(name, options) { 18478 var controller = options.controller || function() {}; 18479 18480 function factory($injector) { 18481 function makeInjectable(fn) { 18482 if (isFunction(fn) || isArray(fn)) { 18483 return /** @this */ function(tElement, tAttrs) { 18484 return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs}); 18485 }; 18486 } else { 18487 return fn; 18488 } 18489 } 18490 18491 var template = (!options.template && !options.templateUrl ? '' : options.template); 18492 var ddo = { 18493 controller: controller, 18494 controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl', 18495 template: makeInjectable(template), 18496 templateUrl: makeInjectable(options.templateUrl), 18497 transclude: options.transclude, 18498 scope: {}, 18499 bindToController: options.bindings || {}, 18500 restrict: 'E', 18501 require: options.require 18502 }; 18503 18504 // Copy annotations (starting with $) over to the DDO 18505 forEach(options, function(val, key) { 18506 if (key.charAt(0) === '$') ddo[key] = val; 18507 }); 18508 18509 return ddo; 18510 } 18511 18512 // TODO(pete) remove the following `forEach` before we release 1.6.0 18513 // The [email protected] looks for the annotations on the controller constructor 18514 // Nothing in Angular looks for annotations on the factory function but we can't remove 18515 // it from 1.5.x yet. 18516 18517 // Copy any annotation properties (starting with $) over to the factory and controller constructor functions 18518 // These could be used by libraries such as the new component router 18519 forEach(options, function(val, key) { 18520 if (key.charAt(0) === '$') { 18521 factory[key] = val; 18522 // Don't try to copy over annotations to named controller 18523 if (isFunction(controller)) controller[key] = val; 18524 } 18525 }); 18526 18527 factory.$inject = ['$injector']; 18528 18529 return this.directive(name, factory); 18530 }; 18531 18532 18533 /** 18534 * @ngdoc method 18535 * @name $compileProvider#aHrefSanitizationWhitelist 18536 * @kind function 18537 * 18538 * @description 18539 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 18540 * urls during a[href] sanitization. 18541 * 18542 * The sanitization is a security measure aimed at preventing XSS attacks via html links. 18543 * 18544 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into 18545 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` 18546 * regular expression. If a match is found, the original url is written into the dom. Otherwise, 18547 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 18548 * 18549 * @param {RegExp=} regexp New regexp to whitelist urls with. 18550 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 18551 * chaining otherwise. 18552 */ 18553 this.aHrefSanitizationWhitelist = function(regexp) { 18554 if (isDefined(regexp)) { 18555 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); 18556 return this; 18557 } else { 18558 return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); 18559 } 18560 }; 18561 18562 18563 /** 18564 * @ngdoc method 18565 * @name $compileProvider#imgSrcSanitizationWhitelist 18566 * @kind function 18567 * 18568 * @description 18569 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 18570 * urls during img[src] sanitization. 18571 * 18572 * The sanitization is a security measure aimed at prevent XSS attacks via html links. 18573 * 18574 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into 18575 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` 18576 * regular expression. If a match is found, the original url is written into the dom. Otherwise, 18577 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 18578 * 18579 * @param {RegExp=} regexp New regexp to whitelist urls with. 18580 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 18581 * chaining otherwise. 18582 */ 18583 this.imgSrcSanitizationWhitelist = function(regexp) { 18584 if (isDefined(regexp)) { 18585 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); 18586 return this; 18587 } else {
18588 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); 18589 } 18590 }; 18591 18592 /** 18593 * @ngdoc method 18594 * @name $compileProvider#debugInfoEnabled 18595 * 18596 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the 18597 * current debugInfoEnabled state 18598 * @returns {*} current value if used as getter or itself (chaining) if used as setter 18599 * 18600 * @kind function 18601 * 18602 * @description 18603 * Call this method to enable/disable various debug runtime information in the compiler such as adding 18604 * binding information and a reference to the current scope on to DOM elements. 18605 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope 18606 * * `ng-binding` CSS class 18607 * * `$binding` data property containing an array of the binding expressions 18608 * 18609 * You may want to disable this in production for a significant performance boost. See 18610 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. 18611 * 18612 * The default value is true. 18613 */ 18614 var debugInfoEnabled = true; 18615 this.debugInfoEnabled = function(enabled) { 18616 if (isDefined(enabled)) { 18617 debugInfoEnabled = enabled; 18618 return this; 18619 } 18620 return debugInfoEnabled; 18621 }; 18622 18623 /** 18624 * @ngdoc method 18625 * @name $compileProvider#preAssignBindingsEnabled 18626 * 18627 * @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the 18628 * current preAssignBindingsEnabled state 18629 * @returns {*} current value if used as getter or itself (chaining) if used as setter 18630 * 18631 * @kind function 18632 * 18633 * @description 18634 * Call this method to enable/disable whether directive controllers are assigned bindings before 18635 * calling the controller's constructor. 18636 * If enabled (true), the compiler assigns the value of each of the bindings to the 18637 * properties of the controller object before the constructor of this object is called. 18638 * 18639 * If disabled (false), the compiler calls the constructor first before assigning bindings. 18640 * 18641 * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x. 18642 */ 18643 var preAssignBindingsEnabled = false; 18644 this.preAssignBindingsEnabled = function(enabled) { 18645 if (isDefined(enabled)) { 18646 preAssignBindingsEnabled = enabled; 18647 return this; 18648 } 18649 return preAssignBindingsEnabled; 18650 }; 18651 18652 18653 var TTL = 10; 18654 /** 18655 * @ngdoc method 18656 * @name $compileProvider#onChangesTtl 18657 * @description 18658 * 18659 * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and 18660 * assuming that the model is unstable. 18661 * 18662 * The current default is 10 iterations. 18663 * 18664 * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result 18665 * in several iterations of calls to these hooks. However if an application needs more than the default 10 18666 * iterations to stabilize then you should investigate what is causing the model to continuously change during 18667 * the `$onChanges` hook execution. 18668 * 18669 * Increasing the TTL could have performance implications, so you should not change it without proper justification. 18670 * 18671 * @param {number} limit The number of `$onChanges` hook iterations. 18672 * @returns {number|object} the current limit (or `this` if called as a setter for chaining) 18673 */ 18674 this.onChangesTtl = function(value) { 18675 if (arguments.length) { 18676 TTL = value; 18677 return this; 18678 } 18679 return TTL; 18680 }; 18681 18682 var commentDirectivesEnabledConfig = true; 18683 /** 18684 * @ngdoc method 18685 * @name $compileProvider#commentDirectivesEnabled 18686 * @description 18687 * 18688 * It indicates to the compiler 18689 * whether or not directives on comments should be compiled. 18690 * Defaults to `true`. 18691 * 18692 * Calling this function with false disables the compilation of directives 18693 * on comments for the whole application. 18694 * This results in a compilation performance gain, 18695 * as the compiler doesn't have to check comments when looking for directives. 18696 * This should however only be used if you are sure that no comment directives are used in 18697 * the application (including any 3rd party directives). 18698 * 18699 * @param {boolean} enabled `false` if the compiler may ignore directives on comments 18700 * @returns {boolean|object} the current value (or `this` if called as a setter for chaining) 18701 */ 18702 this.commentDirectivesEnabled = function(value) { 18703 if (arguments.length) { 18704 commentDirectivesEnabledConfig = value; 18705 return this; 18706 } 18707 return commentDirectivesEnabledConfig; 18708 }; 18709 18710 18711 var cssClassDirectivesEnabledConfig = true; 18712 /** 18713 * @ngdoc method 18714 * @name $compileProvider#cssClassDirectivesEnabled 18715 * @description 18716 * 18717 * It indicates to the compiler 18718 * whether or not directives on element classes should be compiled. 18719 * Defaults to `true`. 18720 *
18721 * Calling this function with false disables the compilation of directives 18722 * on element classes for the whole application. 18723 * This results in a compilation performance gain, 18724 * as the compiler doesn't have to check element classes when looking for directives. 18725 * This should however only be used if you are sure that no class directives are used in 18726 * the application (including any 3rd party directives). 18727 * 18728 * @param {boolean} enabled `false` if the compiler may ignore directives on element classes 18729 * @returns {boolean|object} the current value (or `this` if called as a setter for chaining) 18730 */ 18731 this.cssClassDirectivesEnabled = function(value) { 18732 if (arguments.length) { 18733 cssClassDirectivesEnabledConfig = value; 18734 return this; 18735 } 18736 return cssClassDirectivesEnabledConfig; 18737 }; 18738 18739 this.$get = [ 18740 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse', 18741 '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri', 18742 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse, 18743 $controller, $rootScope, $sce, $animate, $$sanitizeUri) { 18744 18745 var SIMPLE_ATTR_NAME = /^\w/; 18746 var specialAttrHolder = window.document.createElement('div'); 18747 18748 18749 var commentDirectivesEnabled = commentDirectivesEnabledConfig; 18750 var cssClassDirectivesEnabled = cssClassDirectivesEnabledConfig; 18751 18752 18753 var onChangesTtl = TTL; 18754 // The onChanges hooks should all be run together in a single digest 18755 // When changes occur, the call to trigger their hooks will be added to this queue 18756 var onChangesQueue; 18757 18758 // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest 18759 function flushOnChangesQueue() { 18760 try { 18761 if (!(--onChangesTtl)) { 18762 // We have hit the TTL limit so reset everything 18763 onChangesQueue = undefined; 18764 throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL); 18765 } 18766 // We must run this hook in an apply since the $$postDigest runs outside apply 18767 $rootScope.$apply(function() { 18768 var errors = []; 18769 for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) { 18770 try { 18771 onChangesQueue[i](); 18772 } catch (e) { 18773 errors.push(e); 18774 } 18775 } 18776 // Reset the queue to trigger a new schedule next time there is a change 18777 onChangesQueue = undefined; 18778 if (errors.length) { 18779 throw errors; 18780 } 18781 }); 18782 } finally { 18783 onChangesTtl++; 18784 } 18785 } 18786 18787 18788 function Attributes(element, attributesToCopy) { 18789 if (attributesToCopy) { 18790 var keys = Object.keys(attributesToCopy); 18791 var i, l, key; 18792 18793 for (i = 0, l = keys.length; i < l; i++) { 18794 key = keys[i]; 18795 this[key] = attributesToCopy[key]; 18796 } 18797 } else { 18798 this.$attr = {}; 18799 } 18800 18801 this.$$element = element; 18802 } 18803 18804 Attributes.prototype = { 18805 /** 18806 * @ngdoc method 18807 * @name $compile.directive.Attributes#$normalize 18808 * @kind function 18809 * 18810 * @description 18811 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or 18812 * `data-`) to its normalized, camelCase form. 18813 * 18814 * Also there is special case for Moz prefix starting with upper case letter. 18815 * 18816 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} 18817 * 18818 * @param {string} name Name to normalize 18819 */ 18820 $normalize: directiveNormalize, 18821 18822 18823 /** 18824 * @ngdoc method 18825 * @name $compile.directive.Attributes#$addClass 18826 * @kind function 18827 * 18828 * @description 18829 * Adds the CSS class value specified by the classVal parameter to the element. If animations 18830 * are enabled then an animation will be triggered for the class addition. 18831 * 18832 * @param {string} classVal The className value that will be added to the element 18833 */ 18834 $addClass: function(classVal) { 18835 if (classVal && classVal.length > 0) { 18836 $animate.addClass(this.$$element, classVal); 18837 } 18838 }, 18839 18840 /** 18841 * @ngdoc method 18842 * @name $compile.directive.Attributes#$removeClass 18843 * @kind function 18844 *
18845 * @description 18846 * Removes the CSS class value specified by the classVal parameter from the element. If 18847 * animations are enabled then an animation will be triggered for the class removal. 18848 * 18849 * @param {string} classVal The className value that will be removed from the element 18850 */ 18851 $removeClass: function(classVal) { 18852 if (classVal && classVal.length > 0) { 18853 $animate.removeClass(this.$$element, classVal); 18854 } 18855 }, 18856 18857 /** 18858 * @ngdoc method 18859 * @name $compile.directive.Attributes#$updateClass 18860 * @kind function 18861 * 18862 * @description 18863 * Adds and removes the appropriate CSS class values to the element based on the difference 18864 * between the new and old CSS class values (specified as newClasses and oldClasses). 18865 * 18866 * @param {string} newClasses The current CSS className value 18867 * @param {string} oldClasses The former CSS className value 18868 */ 18869 $updateClass: function(newClasses, oldClasses) { 18870 var toAdd = tokenDifference(newClasses, oldClasses); 18871 if (toAdd && toAdd.length) { 18872 $animate.addClass(this.$$element, toAdd); 18873 } 18874 18875 var toRemove = tokenDifference(oldClasses, newClasses); 18876 if (toRemove && toRemove.length) { 18877 $animate.removeClass(this.$$element, toRemove); 18878 } 18879 }, 18880 18881 /** 18882 * Set a normalized attribute on the element in a way such that all directives 18883 * can share the attribute. This function properly handles boolean attributes. 18884 * @param {string} key Normalized key. (ie ngAttribute) 18885 * @param {string|boolean} value The value to set. If `null` attribute will be deleted. 18886 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. 18887 * Defaults to true. 18888 * @param {string=} attrName Optional none normalized name. Defaults to key. 18889 */ 18890 $set: function(key, value, writeAttr, attrName) { 18891 // TODO: decide whether or not to throw an error if "class" 18892 //is set through this function since it may cause $updateClass to 18893 //become unstable. 18894 18895 var node = this.$$element[0], 18896 booleanKey = getBooleanAttrName(node, key), 18897 aliasedKey = getAliasedAttrName(key), 18898 observer = key, 18899 nodeName; 18900 18901 if (booleanKey) { 18902 this.$$element.prop(key, value); 18903 attrName = booleanKey; 18904 } else if (aliasedKey) { 18905 this[aliasedKey] = value; 18906 observer = aliasedKey; 18907 } 18908 18909 this[key] = value; 18910 18911 // translate normalized key to actual key 18912 if (attrName) { 18913 this.$attr[key] = attrName; 18914 } else { 18915 attrName = this.$attr[key]; 18916 if (!attrName) { 18917 this.$attr[key] = attrName = snake_case(key, '-'); 18918 } 18919 } 18920 18921 nodeName = nodeName_(this.$$element); 18922 18923 if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) || 18924 (nodeName === 'img' && key === 'src')) { 18925 // sanitize a[href] and img[src] values 18926 this[key] = value = $$sanitizeUri(value, key === 'src'); 18927 } else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) { 18928 // sanitize img[srcset] values 18929 var result = ''; 18930 18931 // first check if there are spaces because it's not the same pattern 18932 var trimmedSrcset = trim(value); 18933 // ( 999x ,| 999w ,| ,|, ) 18934 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/; 18935 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/; 18936 18937 // split srcset into tuple of uri and descriptor except for the last item 18938 var rawUris = trimmedSrcset.split(pattern); 18939 18940 // for each tuples 18941 var nbrUrisWith2parts = Math.floor(rawUris.length / 2); 18942 for (var i = 0; i < nbrUrisWith2parts; i++) { 18943 var innerIdx = i * 2; 18944 // sanitize the uri 18945 result += $$sanitizeUri(trim(rawUris[innerIdx]), true); 18946 // add the descriptor 18947 result += (' ' + trim(rawUris[innerIdx + 1])); 18948 } 18949 18950 // split the last item into uri and descriptor 18951 var lastTuple = trim(rawUris[i * 2]).split(/\s/); 18952 18953 // sanitize the last uri 18954 result += $$sanitizeUri(trim(lastTuple[0]), true); 18955 18956 // and add the last descriptor if any 18957 if (lastTuple.length === 2) { 18958 result += (' ' + trim(lastTuple[1])); 18959 } 18960 this[key] = value = result; 18961 } 18962 18963 if (writeAttr !== false) { 18964 if (value === null || isUndefined(value)) { 18965 this.$$element.removeAttr(attrName); 18966 } else { 18967 if (SIMPLE_ATTR_NAME.test(attrName)) { 18968 this.$$element.attr(attrName, value); 18969 } else { 18970 setSpecialAttr(this.$$element[0], attrName, value); 18971 } 18972 } 18973 } 18974 18975 // fire observers 18976 var $$observers = this.$$observers; 18977 if ($$observers) {
vendor: 6,838 bytes, lines 18978-19148
18978 forEach($$observers[observer], function(fn) { 18979 try { 18980 fn(value); 18981 } catch (e) { 18982 $exceptionHandler(e); 18983 } 18984 }); 18985 } 18986 }, 18987 18988 18989 /** 18990 * @ngdoc method 18991 * @name $compile.directive.Attributes#$observe 18992 * @kind function 18993 * 18994 * @description 18995 * Observes an interpolated attribute. 18996 * 18997 * The observer function will be invoked once during the next `$digest` following 18998 * compilation. The observer is then invoked whenever the interpolated value 18999 * changes. 19000 * 19001 * @param {string} key Normalized key. (ie ngAttribute) . 19002 * @param {function(interpolatedValue)} fn Function that will be called whenever 19003 the interpolated value of the attribute changes. 19004 * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation 19005 * guide} for more info. 19006 * @returns {function()} Returns a deregistration function for this observer. 19007 */ 19008 $observe: function(key, fn) { 19009 var attrs = this, 19010 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())), 19011 listeners = ($$observers[key] || ($$observers[key] = [])); 19012 19013 listeners.push(fn); 19014 $rootScope.$evalAsync(function() { 19015 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) { 19016 // no one registered attribute interpolation function, so lets call it manually 19017 fn(attrs[key]); 19018 } 19019 }); 19020 19021 return function() { 19022 arrayRemove(listeners, fn); 19023 }; 19024 } 19025 }; 19026 19027 function setSpecialAttr(element, attrName, value) { 19028 // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute` 19029 // so we have to jump through some hoops to get such an attribute 19030 // https://github.com/angular/angular.js/pull/13318 19031 specialAttrHolder.innerHTML = '<span ' + attrName + '>'; 19032 var attributes = specialAttrHolder.firstChild.attributes; 19033 var attribute = attributes[0]; 19034 // We have to remove the attribute from its container element before we can add it to the destination element 19035 attributes.removeNamedItem(attribute.name); 19036 attribute.value = value; 19037 element.attributes.setNamedItem(attribute); 19038 } 19039 19040 function safeAddClass($element, className) { 19041 try { 19042 $element.addClass(className); 19043 } catch (e) { 19044 // ignore, since it means that we are trying to set class on 19045 // SVG element, where class name is read-only. 19046 } 19047 } 19048 19049 19050 var startSymbol = $interpolate.startSymbol(), 19051 endSymbol = $interpolate.endSymbol(), 19052 denormalizeTemplate = (startSymbol === '{{' && endSymbol === '}}') 19053 ? identity 19054 : function denormalizeTemplate(template) { 19055 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); 19056 }, 19057 NG_ATTR_BINDING = /^ngAttr[A-Z]/; 19058 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/; 19059 19060 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) { 19061 var bindings = $element.data('$binding') || []; 19062 19063 if (isArray(binding)) { 19064 bindings = bindings.concat(binding); 19065 } else { 19066 bindings.push(binding); 19067 } 19068 19069 $element.data('$binding', bindings); 19070 } : noop; 19071 19072 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) { 19073 safeAddClass($element, 'ng-binding'); 19074 } : noop; 19075 19076 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) { 19077 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope'; 19078 $element.data(dataName, scope); 19079 } : noop; 19080 19081 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) { 19082 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope'); 19083 } : noop; 19084 19085 compile.$$createComment = function(directiveName, comment) { 19086 var content = ''; 19087 if (debugInfoEnabled) { 19088 content = ' ' + (directiveName || '') + ': '; 19089 if (comment) content += comment + ' '; 19090 } 19091 return window.document.createComment(content); 19092 }; 19093 19094 return compile; 19095 19096 //================================ 19097 19098 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, 19099 previousCompileContext) { 19100 if (!($compileNodes instanceof jqLite)) { 19101 // jquery always rewraps, whereas we need to preserve the original selector so that we can 19102 // modify it. 19103 $compileNodes = jqLite($compileNodes); 19104 } 19105 var compositeLinkFn = 19106 compileNodes($compileNodes, transcludeFn, $compileNodes, 19107 maxPriority, ignoreDirective, previousCompileContext); 19108 compile.$$addScopeClass($compileNodes); 19109 var namespace = null; 19110 return function publicLinkFn(scope, cloneConnectFn, options) { 19111 if (!$compileNodes) { 19112 throw $compileMinErr('multilink', 'This element has already been linked.'); 19113 } 19114 assertArg(scope, 'scope'); 19115 19116 if (previousCompileContext && previousCompileContext.needsNewScope) { 19117 // A parent directive did a replace and a directive on this element asked 19118 // for transclusion, which caused us to lose a layer of element on which 19119 // we could hold the new transclusion scope, so we will create it manually 19120 // here. 19121 scope = scope.$parent.$new(); 19122 } 19123 19124 options = options || {}; 19125 var parentBoundTranscludeFn = options.parentBoundTranscludeFn, 19126 transcludeControllers = options.transcludeControllers, 19127 futureParentElement = options.futureParentElement; 19128 19129 // When `parentBoundTranscludeFn` is passed, it is a 19130 // `controllersBoundTransclude` function (it was previously passed 19131 // as `transclude` to directive.link) so we must unwrap it to get 19132 // its `boundTranscludeFn` 19133 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) { 19134 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude; 19135 } 19136 19137 if (!namespace) { 19138 namespace = detectNamespaceForChildElements(futureParentElement); 19139 } 19140 var $linkNode; 19141 if (namespace !== 'html') { 19142 // When using a directive with replace:true and templateUrl the $compileNodes 19143 // (or a child element inside of them) 19144 // might change, so we need to recreate the namespace adapted compileNodes 19145 // for call to the link function. 19146 // Note: This will already clone the nodes... 19147 $linkNode = jqLite( 19148 wrapTemplate(namespace, jqLite('<
vendor: 9,507 bytes, lines 19148-19393
19148div>').append($compileNodes).html()) 19149 ); 19150 } else if (cloneConnectFn) { 19151 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart 19152 // and sometimes changes the structure of the DOM. 19153 $linkNode = JQLitePrototype.clone.call($compileNodes); 19154 } else { 19155 $linkNode = $compileNodes; 19156 } 19157 19158 if (transcludeControllers) { 19159 for (var controllerName in transcludeControllers) { 19160 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance); 19161 } 19162 } 19163 19164 compile.$$addScopeInfo($linkNode, scope); 19165 19166 if (cloneConnectFn) cloneConnectFn($linkNode, scope); 19167 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); 19168 19169 if (!cloneConnectFn) { 19170 $compileNodes = compositeLinkFn = null; 19171 } 19172 return $linkNode; 19173 }; 19174 } 19175 19176 function detectNamespaceForChildElements(parentElement) { 19177 // TODO: Make this detect MathML as well... 19178 var node = parentElement && parentElement[0]; 19179 if (!node) { 19180 return 'html'; 19181 } else { 19182 return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html'; 19183 } 19184 } 19185 19186 /** 19187 * Compile function matches each node in nodeList against the directives. Once all directives 19188 * for a particular node are collected their compile functions are executed. The compile 19189 * functions return values - the linking functions - are combined into a composite linking 19190 * function, which is the a linking function for the node. 19191 * 19192 * @param {NodeList} nodeList an array of nodes or NodeList to compile 19193 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the 19194 * scope argument is auto-generated to the new child of the transcluded parent scope. 19195 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then 19196 * the rootElement must be set the jqLite collection of the compile root. This is 19197 * needed so that the jqLite collection items can be replaced with widgets. 19198 * @param {number=} maxPriority Max directive priority. 19199 * @returns {Function} A composite linking function of all of the matched directives or null. 19200 */ 19201 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, 19202 previousCompileContext) { 19203 var linkFns = [], 19204 // `nodeList` can be either an element's `.childNodes` (live NodeList) 19205 // or a jqLite/jQuery collection or an array 19206 notLiveList = isArray(nodeList) || (nodeList instanceof jqLite), 19207 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound; 19208 19209 19210 for (var i = 0; i < nodeList.length; i++) { 19211 attrs = new Attributes(); 19212 19213 // Support: IE 11 only 19214 // Workaround for #11781 and #14924 19215 if (msie === 11) { 19216 mergeConsecutiveTextNodes(nodeList, i, notLiveList); 19217 } 19218 19219 // We must always refer to `nodeList[i]` hereafter, 19220 // since the nodes can be replaced underneath us. 19221 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, 19222 ignoreDirective); 19223 19224 nodeLinkFn = (directives.length) 19225 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, 19226 null, [], [], previousCompileContext) 19227 : null; 19228 19229 if (nodeLinkFn && nodeLinkFn.scope) { 19230 compile.$$addScopeClass(attrs.$$element); 19231 } 19232 19233 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || 19234 !(childNodes = nodeList[i].childNodes) || 19235 !childNodes.length) 19236 ? null 19237 : compileNodes(childNodes, 19238 nodeLinkFn ? ( 19239 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement) 19240 && nodeLinkFn.transclude) : transcludeFn); 19241 19242 if (nodeLinkFn || childLinkFn) { 19243 linkFns.push(i, nodeLinkFn, childLinkFn); 19244 linkFnFound = true; 19245 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn; 19246 } 19247 19248 //use the previous context only for the first element in the virtual group 19249 previousCompileContext = null; 19250 } 19251 19252 // return a linking function if we have found anything, null otherwise 19253 return linkFnFound ? compositeLinkFn : null; 19254 19255 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) { 19256 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn; 19257 var stableNodeList; 19258 19259 19260 if (nodeLinkFnFound) { 19261 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our 19262 // offsets don't get screwed up 19263 var nodeListLength = nodeList.length; 19264 stableNodeList = new Array(nodeListLength); 19265 19266 // create a sparse array by only copying the elements which have a linkFn 19267 for (i = 0; i < linkFns.length; i += 3) { 19268 idx = linkFns[i]; 19269 stableNodeList[idx] = nodeList[idx]; 19270 } 19271 } else { 19272 stableNodeList = nodeList; 19273 } 19274 19275 for (i = 0, ii = linkFns.length; i < ii;) { 19276 node = stableNodeList[linkFns[i++]]; 19277 nodeLinkFn = linkFns[i++]; 19278 childLinkFn = linkFns[i++]; 19279 19280 if (nodeLinkFn) { 19281 if (nodeLinkFn.scope) { 19282 childScope = scope.$new(); 19283 compile.$$addScopeInfo(jqLite(node), childScope); 19284 } else { 19285 childScope = scope; 19286 } 19287 19288 if (nodeLinkFn.transcludeOnThisElement) { 19289 childBoundTranscludeFn = createBoundTranscludeFn( 19290 scope, nodeLinkFn.transclude, parentBoundTranscludeFn); 19291 19292 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { 19293 childBoundTranscludeFn = parentBoundTranscludeFn; 19294 19295 } else if (!parentBoundTranscludeFn && transcludeFn) { 19296 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn); 19297 19298 } else { 19299 childBoundTranscludeFn = null; 19300 } 19301 19302 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn); 19303 19304 } else if (childLinkFn) { 19305 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); 19306 } 19307 } 19308 } 19309 } 19310 19311 function mergeConsecutiveTextNodes(nodeList, idx, notLiveList) { 19312 var node = nodeList[idx]; 19313 var parent = node.parentNode; 19314 var sibling; 19315 19316 if (node.nodeType !== NODE_TYPE_TEXT) { 19317 return; 19318 } 19319 19320 while (true) { 19321 sibling = parent ? node.nextSibling : nodeList[idx + 1]; 19322 if (!sibling || sibling.nodeType !== NODE_TYPE_TEXT) { 19323 break; 19324 } 19325 19326 node.nodeValue = node.nodeValue + sibling.nodeValue; 19327 19328 if (sibling.parentNode) { 19329 sibling.parentNode.removeChild(sibling); 19330 } 19331 if (notLiveList && sibling === nodeList[idx + 1]) { 19332 nodeList.splice(idx + 1, 1); 19333 } 19334 } 19335 } 19336 19337 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) { 19338 function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) { 19339 19340 if (!transcludedScope) { 19341 transcludedScope = scope.$new(false, containingScope); 19342 transcludedScope.$$transcluded = true; 19343 } 19344 19345 return transcludeFn(transcludedScope, cloneFn, { 19346 parentBoundTranscludeFn: previousBoundTranscludeFn, 19347 transcludeControllers: controllers, 19348 futureParentElement: futureParentElement 19349 }); 19350 } 19351 19352 // We need to attach the transclusion slots onto the `boundTranscludeFn` 19353 // so that they are available inside the `controllersBoundTransclude` function 19354 var boundSlots = boundTranscludeFn.$$slots = createMap(); 19355 for (var slotName in transcludeFn.$$slots) { 19356 if (transcludeFn.$$slots[slotName]) { 19357 boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn); 19358 } else { 19359 boundSlots[slotName] = null; 19360 } 19361 } 19362 19363 return boundTranscludeFn; 19364 } 19365 19366 /** 19367 * Looks for directives on the given node and adds them to the directive collection which is 19368 * sorted. 19369 * 19370 * @param node Node to search. 19371 * @param directives An array to which the directives are added to. This array is sorted before 19372 * the function returns. 19373 * @param attrs The shared attrs object which is used to populate the normalized attributes. 19374 * @param {number=} maxPriority Max directive priority. 19375 */ 19376 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) { 19377 var nodeType = node.nodeType, 19378 attrsMap = attrs.$attr, 19379 match, 19380 nodeName, 19381 className; 19382 19383 switch (nodeType) { 19384 case NODE_TYPE_ELEMENT: /* Element */ 19385 19386 nodeName = nodeName_(node); 19387 19388 // use the node name: <directive> 19389 addDirective(directives, 19390 directiveNormalize(nodeName), 'E', maxPriority, ignoreDirective); 19391 19392 // iterate over the attributes 19393 for (var attr, name, nName,
19393ngAttrName, value, isNgAttr, nAttrs = node.attributes, 19394 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { 19395 var attrStartName = false;
vendor: 13,392 bytes, lines 19396-19701
19396 var attrEndName = false; 19397 19398 attr = nAttrs[j]; 19399 name = attr.name; 19400 value = attr.value; 19401 19402 // support ngAttr attribute binding 19403 ngAttrName = directiveNormalize(name); 19404 isNgAttr = NG_ATTR_BINDING.test(ngAttrName); 19405 if (isNgAttr) { 19406 name = name.replace(PREFIX_REGEXP, '') 19407 .substr(8).replace(/_(.)/g, function(match, letter) { 19408 return letter.toUpperCase(); 19409 }); 19410 } 19411 19412 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE); 19413 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) { 19414 attrStartName = name; 19415 attrEndName = name.substr(0, name.length - 5) + 'end'; 19416 name = name.substr(0, name.length - 6); 19417 } 19418 19419 nName = directiveNormalize(name.toLowerCase()); 19420 attrsMap[nName] = name; 19421 if (isNgAttr || !attrs.hasOwnProperty(nName)) { 19422 attrs[nName] = value; 19423 if (getBooleanAttrName(node, nName)) { 19424 attrs[nName] = true; // presence means true 19425 } 19426 } 19427 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr); 19428 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, 19429 attrEndName); 19430 } 19431 19432 if (nodeName === 'input' && node.getAttribute('type') === 'hidden') { 19433 // Hidden input elements can have strange behaviour when navigating back to the page 19434 // This tells the browser not to try to cache and reinstate previous values 19435 node.setAttribute('autocomplete', 'off'); 19436 } 19437 19438 // use class as directive 19439 if (!cssClassDirectivesEnabled) break; 19440 className = node.className; 19441 if (isObject(className)) { 19442 // Maybe SVGAnimatedString 19443 className = className.animVal; 19444 } 19445 if (isString(className) && className !== '') { 19446 while ((match = CLASS_DIRECTIVE_REGEXP.exec(className))) { 19447 nName = directiveNormalize(match[2]); 19448 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { 19449 attrs[nName] = trim(match[3]); 19450 } 19451 className = className.substr(match.index + match[0].length); 19452 } 19453 } 19454 break; 19455 case NODE_TYPE_TEXT: /* Text Node */ 19456 addTextInterpolateDirective(directives, node.nodeValue); 19457 break; 19458 case NODE_TYPE_COMMENT: /* Comment */ 19459 if (!commentDirectivesEnabled) break; 19460 collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective); 19461 break; 19462 } 19463 19464 directives.sort(byPriority); 19465 return directives; 19466 } 19467 19468 function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) { 19469 // function created because of performance, try/catch disables 19470 // the optimization of the whole function #14848 19471 try { 19472 var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); 19473 if (match) { 19474 var nName = directiveNormalize(match[1]); 19475 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { 19476 attrs[nName] = trim(match[2]); 19477 } 19478 } 19479 } catch (e) { 19480 // turns out that under some circumstances IE9 throws errors when one attempts to read 19481 // comment's node value. 19482 // Just ignore it and continue. (Can't seem to reproduce in test case.) 19483 } 19484 } 19485 19486 /** 19487 * Given a node with a directive-start it collects all of the siblings until it finds 19488 * directive-end. 19489 * @param node 19490 * @param attrStart 19491 * @param attrEnd 19492 * @returns {*} 19493 */ 19494 function groupScan(node, attrStart, attrEnd) { 19495 var nodes = []; 19496 var depth = 0; 19497 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { 19498 do { 19499 if (!node) { 19500 throw $compileMinErr('uterdir', 19501 'Unterminated attribute, found \'{0}\' but no matching \'{1}\' found.', 19502 attrStart, attrEnd); 19503 } 19504 if (node.nodeType === NODE_TYPE_ELEMENT) { 19505 if (node.hasAttribute(attrStart)) depth++; 19506 if (node.hasAttribute(attrEnd)) depth--; 19507 } 19508 nodes.push(node); 19509 node = node.nextSibling; 19510 } while (depth > 0); 19511 } else { 19512 nodes.push(node); 19513 } 19514 19515 return jqLite(nodes); 19516 } 19517 19518 /** 19519 * Wrapper for linking function which converts normal linking function into a grouped 19520 * linking function. 19521 * @param linkFn 19522 * @param attrStart 19523 * @param attrEnd 19524 * @returns {Function} 19525 */ 19526 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { 19527 return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) { 19528 element = groupScan(element[0], attrStart, attrEnd); 19529 return linkFn(scope, element, attrs, controllers, transcludeFn); 19530 }; 19531 } 19532 19533 /** 19534 * A function generator that is used to support both eager and lazy compilation 19535 * linking function. 19536 * @param eager 19537 * @param $compileNodes 19538 * @param transcludeFn 19539 * @param maxPriority 19540 * @param ignoreDirective 19541 * @param previousCompileContext 19542 * @returns {Function} 19543 */ 19544 function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) { 19545 var compiled; 19546 19547 if (eager) { 19548 return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext); 19549 } 19550 return /** @this */ function lazyCompilation() { 19551 if (!compiled) { 19552 compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext); 19553 19554 // Null out all of these references in order to make them eligible for garbage collection 19555 // since this is a potentially long lived closure 19556 $compileNodes = transcludeFn = previousCompileContext = null; 19557 } 19558 return compiled.apply(this, arguments); 19559 }; 19560 } 19561 19562 /** 19563 * Once the directives have been collected, their compile functions are executed. This method 19564 * is responsible for inlining directive templates as well as terminating the application 19565 * of the directives if the terminal directive has been reached. 19566 * 19567 * @param {Array} directives Array of collected directives to execute their compile function. 19568 * this needs to be pre-sorted by priority order. 19569 * @param {Node} compileNode The raw DOM node to apply the compile functions to 19570 * @param {Object} templateAttrs The shared attribute function 19571 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the 19572 * scope argument is auto-generated to the new 19573 * child of the transcluded parent scope. 19574 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this 19575 * argument has the root jqLite array so that we can replace nodes 19576 * on it. 19577 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when 19578 * compiling the transclusion. 19579 * @param {Array.<Function>} preLinkFns 19580 * @param {Array.<Function>} postLinkFns 19581 * @param {Object} previousCompileContext Context used for previous compilation of the current 19582 * node 19583 * @returns {Function} linkFn 19584 */ 19585 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, 19586 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, 19587 previousCompileContext) { 19588 previousCompileContext = previousCompileContext || {}; 19589 19590 var terminalPriority = -Number.MAX_VALUE, 19591 newScopeDirective = previousCompileContext.newScopeDirective, 19592 controllerDirectives = previousCompileContext.controllerDirectives, 19593 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, 19594 templateDirective = previousCompileContext.templateDirective, 19595 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, 19596 hasTranscludeDirective = false, 19597 hasTemplate = false, 19598 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, 19599 $compileNode = templateAttrs.$$element = jqLite(compileNode), 19600 directive, 19601 directiveName, 19602 $template, 19603 replaceDirective = originalReplaceDirective, 19604 childTranscludeFn = transcludeFn, 19605 linkFn, 19606 didScanForMultipleTransclusion = false, 19607 mightHaveMultipleTransclusionError = false, 19608 directiveValue; 19609 19610 // executes all directives on the current element 19611 for (var i = 0, ii = directives.length; i < ii; i++) { 19612 directive = directives[i]; 19613 var attrStart = directive.$$start; 19614 var attrEnd = directive.$$end; 19615 19616 // collect multiblock sections 19617 if (attrStart) { 19618 $compileNode = groupScan(compileNode, attrStart, attrEnd); 19619 } 19620 $template = undefined; 19621 19622 if (terminalPriority > directive.priority) { 19623 break; // prevent further processing of directives 19624 } 19625 19626 directiveValue = directive.scope; 19627 19628 if (directiveValue) { 19629 19630 // skip the check for directives with async templates, we'll check the derived sync 19631 // directive when the template arrives 19632 if (!directive.templateUrl) { 19633 if (isObject(directiveValue)) { 19634 // This directive is trying to add an isolated scope. 19635 // Check that there is no scope of any kind already 19636 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective, 19637 directive, $compileNode); 19638 newIsolateScopeDirective = directive; 19639 } else { 19640 // This directive is trying to add a child scope. 19641 // Check that there is no isolated scope already 19642 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, 19643 $compileNode); 19644 } 19645 } 19646 19647 newScopeDirective = newScopeDirective || directive; 19648 } 19649 19650 directiveName = directive.name; 19651 19652 // If we encounter a condition that can result in transclusion on the directive, 19653 // then scan ahead in the remaining directives for others that may cause a multiple 19654 // transclusion error to be thrown during the compilation process. If a matching directive 19655 // is found, then we know that when we encounter a transcluded directive, we need to eagerly 19656 // compile the `transclude` function rather than doing it lazily in order to throw 19657 // exceptions at the correct time 19658 if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template)) 19659 || (directive.transclude && !directive.$$tlb))) { 19660 var candidateDirective; 19661 19662 for (var scanningIndex = i + 1; (candidateDirective = directives[scanningIndex++]);) { 19663 if ((candidateDirective.transclude && !candidateDirective.$$tlb) 19664 || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) { 19665 mightHaveMultipleTransclusionError = true; 19666 break; 19667 } 19668 } 19669 19670 didScanForMultipleTransclusion = true; 19671 } 19672 19673 if (!directive.templateUrl && directive.controller) { 19674 controllerDirectives = controllerDirectives || createMap(); 19675 assertNoDuplicate('\'' + directiveName + '\' controller', 19676 controllerDirectives[directiveName], directive, $compileNode); 19677 controllerDirectives[directiveName] = directive; 19678 } 19679 19680 directiveValue = directive.transclude; 19681 19682 if (directiveValue) { 19683 hasTranscludeDirective = true; 19684 19685 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. 19686 // This option should only be used by directives that know how to safely handle element transclusion, 19687 // where the transcluded nodes are added or replaced after linking. 19688 if (!directive.$$tlb) { 19689 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); 19690 nonTlbTranscludeDirective = directive; 19691 } 19692 19693 if (directiveValue === 'element') { 19694 hasElementTranscludeDirective = true; 19695 terminalPriority = directive.priority; 19696 $template = $compileNode; 19697 $compileNode = templateAttrs.$$element = 19698 jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName])); 19699 compileNode = $compileNode[0]; 19700 replaceWith(jqCollection, sliceArgs($template), compileNode); 19701
19702 // Support: Chrome < 50 19703 // https://github.com/angular/angular.js/issues/14041 19704 19705 // In the versions of V8 prior to Chrome 50, the document fragment that is created 19706 // in the `replaceWith` function is improperly garbage collected despite still 19707 // being referenced by the `parentNode` property of all of the child nodes. By adding 19708 // a reference to the fragment via a different property, we can avoid that incorrect 19709 // behavior. 19710 // TODO: remove this line after Chrome 50 has been released 19711 $template[0].$$parentNode = $template[0].parentNode; 19712 19713 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority, 19714 replaceDirective && replaceDirective.name, { 19715 // Don't pass in: 19716 // - controllerDirectives - otherwise we'll create duplicates controllers 19717 // - newIsolateScopeDirective or templateDirective - comb
vendor: 12,814 bytes, lines 19717-19997
19717ining templates with 19718 // element transclusion doesn't make sense. 19719 // 19720 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion 19721 // on the same element more than once. 19722 nonTlbTranscludeDirective: nonTlbTranscludeDirective 19723 }); 19724 } else { 19725 19726 var slots = createMap(); 19727 19728 if (!isObject(directiveValue)) { 19729 $template = jqLite(jqLiteClone(compileNode)).contents(); 19730 } else { 19731 19732 // We have transclusion slots, 19733 // collect them up, compile them and store their transclusion functions 19734 $template = []; 19735 19736 var slotMap = createMap(); 19737 var filledSlots = createMap(); 19738 19739 // Parse the element selectors 19740 forEach(directiveValue, function(elementSelector, slotName) { 19741 // If an element selector starts with a ? then it is optional 19742 var optional = (elementSelector.charAt(0) === '?'); 19743 elementSelector = optional ? elementSelector.substring(1) : elementSelector; 19744 19745 slotMap[elementSelector] = slotName; 19746 19747 // We explicitly assign `null` since this implies that a slot was defined but not filled. 19748 // Later when calling boundTransclusion functions with a slot name we only error if the 19749 // slot is `undefined` 19750 slots[slotName] = null; 19751 19752 // filledSlots contains `true` for all slots that are either optional or have been 19753 // filled. This is used to check that we have not missed any required slots 19754 filledSlots[slotName] = optional; 19755 }); 19756 19757 // Add the matching elements into their slot 19758 forEach($compileNode.contents(), function(node) { 19759 var slotName = slotMap[directiveNormalize(nodeName_(node))]; 19760 if (slotName) { 19761 filledSlots[slotName] = true; 19762 slots[slotName] = slots[slotName] || []; 19763 slots[slotName].push(node); 19764 } else { 19765 $template.push(node); 19766 } 19767 }); 19768 19769 // Check for required slots that were not filled 19770 forEach(filledSlots, function(filled, slotName) { 19771 if (!filled) { 19772 throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName); 19773 } 19774 }); 19775 19776 for (var slotName in slots) { 19777 if (slots[slotName]) { 19778 // Only define a transclusion function if the slot was filled 19779 slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn); 19780 } 19781 } 19782 } 19783 19784 $compileNode.empty(); // clear contents 19785 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined, 19786 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope}); 19787 childTranscludeFn.$$slots = slots; 19788 } 19789 } 19790 19791 if (directive.template) { 19792 hasTemplate = true; 19793 assertNoDuplicate('template', templateDirective, directive, $compileNode); 19794 templateDirective = directive; 19795 19796 directiveValue = (isFunction(directive.template)) 19797 ? directive.template($compileNode, templateAttrs) 19798 : directive.template; 19799 19800 directiveValue = denormalizeTemplate(directiveValue); 19801 19802 if (directive.replace) { 19803 replaceDirective = directive; 19804 if (jqLiteIsTextNode(directiveValue)) { 19805 $template = []; 19806 } else { 19807 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue))); 19808 } 19809 compileNode = $template[0]; 19810 19811 if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { 19812 throw $compileMinErr('tplrt', 19813 'Template for directive \'{0}\' must have exactly one root element. {1}', 19814 directiveName, ''); 19815 } 19816 19817 replaceWith(jqCollection, $compileNode, compileNode); 19818 19819 var newTemplateAttrs = {$attr: {}}; 19820 19821 // combine directives from the original node and from the template: 19822 // - take the array of directives for this element 19823 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) 19824 // - collect directives from the template and sort them by priority 19825 // - combine directives as: processed + template + unprocessed 19826 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); 19827 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); 19828 19829 if (newIsolateScopeDirective || newScopeDirective) { 19830 // The original directive caused the current element to be replaced but this element 19831 // also needs to have a new scope, so we need to tell the template directives 19832 // that they would need to get their scope from further up, if they require transclusion 19833 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective); 19834 } 19835 directives = directives.concat(templateDirectives).concat(unprocessedDirectives); 19836 mergeTemplateAttributes(templateAttrs, newTemplateAttrs); 19837 19838 ii = directives.length; 19839 } else { 19840 $compileNode.html(directiveValue); 19841 } 19842 } 19843 19844 if (directive.templateUrl) { 19845 hasTemplate = true; 19846 assertNoDuplicate('template', templateDirective, directive, $compileNode); 19847 templateDirective = directive; 19848 19849 if (directive.replace) { 19850 replaceDirective = directive; 19851 } 19852 19853 // eslint-disable-next-line no-func-assign 19854 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, 19855 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { 19856 controllerDirectives: controllerDirectives, 19857 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective, 19858 newIsolateScopeDirective: newIsolateScopeDirective, 19859 templateDirective: templateDirective, 19860 nonTlbTranscludeDirective: nonTlbTranscludeDirective 19861 }); 19862 ii = directives.length; 19863 } else if (directive.compile) { 19864 try { 19865 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); 19866 var context = directive.$$originalDirective || directive; 19867 if (isFunction(linkFn)) { 19868 addLinkFns(null, bind(context, linkFn), attrStart, attrEnd); 19869 } else if (linkFn) { 19870 addLinkFns(bind(context, linkFn.pre), bind(context, linkFn.post), attrStart, attrEnd); 19871 } 19872 } catch (e) { 19873 $exceptionHandler(e, startingTag($compileNode)); 19874 } 19875 } 19876 19877 if (directive.terminal) { 19878 nodeLinkFn.terminal = true; 19879 terminalPriority = Math.max(terminalPriority, directive.priority); 19880 } 19881 19882 } 19883 19884 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; 19885 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; 19886 nodeLinkFn.templateOnThisElement = hasTemplate; 19887 nodeLinkFn.transclude = childTranscludeFn; 19888 19889 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; 19890 19891 // might be normal or delayed nodeLinkFn depending on if templateUrl is present 19892 return nodeLinkFn; 19893 19894 //////////////////// 19895 19896 function addLinkFns(pre, post, attrStart, attrEnd) { 19897 if (pre) { 19898 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); 19899 pre.require = directive.require; 19900 pre.directiveName = directiveName; 19901 if (newIsolateScopeDirective === directive || directive.$$isolateScope) { 19902 pre = cloneAndAnnotateFn(pre, {isolateScope: true}); 19903 } 19904 preLinkFns.push(pre); 19905 } 19906 if (post) { 19907 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); 19908 post.require = directive.require; 19909 post.directiveName = directiveName; 19910 if (newIsolateScopeDirective === directive || directive.$$isolateScope) { 19911 post = cloneAndAnnotateFn(post, {isolateScope: true}); 19912 } 19913 postLinkFns.push(post); 19914 } 19915 } 19916 19917 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { 19918 var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element, 19919 attrs, scopeBindingInfo; 19920 19921 if (compileNode === linkNode) { 19922 attrs = templateAttrs; 19923 $element = templateAttrs.$$element; 19924 } else { 19925 $element = jqLite(linkNode); 19926 attrs = new Attributes($element, templateAttrs); 19927 } 19928 19929 controllerScope = scope; 19930 if (newIsolateScopeDirective) { 19931 isolateScope = scope.$new(true); 19932 } else if (newScopeDirective) { 19933 controllerScope = scope.$parent; 19934 } 19935 19936 if (boundTranscludeFn) { 19937 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn` 19938 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn` 19939 transcludeFn = controllersBoundTransclude; 19940 transcludeFn.$$boundTransclude = boundTranscludeFn; 19941 // expose the slots on the `$transclude` function 19942 transcludeFn.isSlotFilled = function(slotName) { 19943 return !!boundTranscludeFn.$$slots[slotName]; 19944 }; 19945 } 19946 19947 if (controllerDirectives) { 19948 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective); 19949 } 19950 19951 if (newIsolateScopeDirective) { 19952 // Initialize isolate scope bindings for new isolate scope directive. 19953 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || 19954 templateDirective === newIsolateScopeDirective.$$originalDirective))); 19955 compile.$$addScopeClass($element, true); 19956 isolateScope.$$isolateBindings = 19957 newIsolateScopeDirective.$$isolateBindings; 19958 scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope, 19959 isolateScope.$$isolateBindings, 19960 newIsolateScopeDirective); 19961 if (scopeBindingInfo.removeWatches) { 19962 isolateScope.$on('$destroy', scopeBindingInfo.removeWatches); 19963 } 19964 } 19965 19966 // Initialize bindToController bindings 19967 for (var name in elementControllers) { 19968 var controllerDirective = controllerDirectives[name]; 19969 var controller = elementControllers[name]; 19970 var bindings = controllerDirective.$$bindings.bindToController; 19971 19972 if (preAssignBindingsEnabled) { 19973 if (bindings) { 19974 controller.bindingInfo = 19975 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); 19976 } else { 19977 controller.bindingInfo = {}; 19978 } 19979 19980 var controllerResult = controller(); 19981 if (controllerResult !== controller.instance) { 19982 // If the controller constructor has a return value, overwrite the instance 19983 // from setupControllers 19984 controller.instance = controllerResult; 19985 $element.data('$' + controllerDirective.name + 'Controller', controllerResult); 19986 if (controller.bindingInfo.removeWatches) { 19987 controller.bindingInfo.removeWatches(); 19988 } 19989 controller.bindingInfo = 19990 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); 19991 } 19992 } else { 19993 controller.instance = controller(); 19994 $element.data('$' + controllerDirective.name + 'Controller', controller.instance); 19995 controller.bindingInfo = 19996 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); 19997 }
vendor: 5,779 bytes, lines 19998-20138
19998 } 19999 20000 // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy 20001 forEach(controllerDirectives, function(controllerDirective, name) { 20002 var require = controllerDirective.require; 20003 if (controllerDirective.bindToController && !isArray(require) && isObject(require)) { 20004 extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers)); 20005 } 20006 }); 20007 20008 // Handle the init and destroy lifecycle hooks on all controllers that have them 20009 forEach(elementControllers, function(controller) { 20010 var controllerInstance = controller.instance; 20011 if (isFunction(controllerInstance.$onChanges)) { 20012 try { 20013 controllerInstance.$onChanges(controller.bindingInfo.initialChanges); 20014 } catch (e) { 20015 $exceptionHandler(e); 20016 } 20017 } 20018 if (isFunction(controllerInstance.$onInit)) { 20019 try { 20020 controllerInstance.$onInit(); 20021 } catch (e) { 20022 $exceptionHandler(e); 20023 } 20024 } 20025 if (isFunction(controllerInstance.$doCheck)) { 20026 controllerScope.$watch(function() { controllerInstance.$doCheck(); }); 20027 controllerInstance.$doCheck(); 20028 } 20029 if (isFunction(controllerInstance.$onDestroy)) { 20030 controllerScope.$on('$destroy', function callOnDestroyHook() { 20031 controllerInstance.$onDestroy(); 20032 }); 20033 } 20034 }); 20035 20036 // PRELINKING 20037 for (i = 0, ii = preLinkFns.length; i < ii; i++) { 20038 linkFn = preLinkFns[i]; 20039 invokeLinkFn(linkFn, 20040 linkFn.isolateScope ? isolateScope : scope, 20041 $element, 20042 attrs, 20043 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), 20044 transcludeFn 20045 ); 20046 } 20047 20048 // RECURSION 20049 // We only pass the isolate scope, if the isolate directive has a template, 20050 // otherwise the child elements do not belong to the isolate directive. 20051 var scopeToChild = scope; 20052 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { 20053 scopeToChild = isolateScope; 20054 } 20055 if (childLinkFn) { 20056 childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); 20057 } 20058 20059 // POSTLINKING 20060 for (i = postLinkFns.length - 1; i >= 0; i--) { 20061 linkFn = postLinkFns[i]; 20062 invokeLinkFn(linkFn, 20063 linkFn.isolateScope ? isolateScope : scope, 20064 $element, 20065 attrs, 20066 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), 20067 transcludeFn 20068 ); 20069 } 20070 20071 // Trigger $postLink lifecycle hooks 20072 forEach(elementControllers, function(controller) { 20073 var controllerInstance = controller.instance; 20074 if (isFunction(controllerInstance.$postLink)) { 20075 controllerInstance.$postLink(); 20076 } 20077 }); 20078 20079 // This is the function that is injected as `$transclude`. 20080 // Note: all arguments are optional! 20081 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) { 20082 var transcludeControllers; 20083 // No scope passed in: 20084 if (!isScope(scope)) { 20085 slotName = futureParentElement; 20086 futureParentElement = cloneAttachFn; 20087 cloneAttachFn = scope; 20088 scope = undefined; 20089 } 20090 20091 if (hasElementTranscludeDirective) { 20092 transcludeControllers = elementControllers; 20093 } 20094 if (!futureParentElement) { 20095 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element; 20096 } 20097 if (slotName) { 20098 // slotTranscludeFn can be one of three things: 20099 // * a transclude function - a filled slot 20100 // * `null` - an optional slot that was not filled 20101 // * `undefined` - a slot that was not declared (i.e. invalid) 20102 var slotTranscludeFn = boundTranscludeFn.$$slots[slotName]; 20103 if (slotTranscludeFn) { 20104 return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); 20105 } else if (isUndefined(slotTranscludeFn)) { 20106 throw $compileMinErr('noslot', 20107 'No parent directive that requires a transclusion with slot name "{0}". ' + 20108 'Element: {1}', 20109 slotName, startingTag($element)); 20110 } 20111 } else { 20112 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); 20113 } 20114 } 20115 } 20116 } 20117 20118 function getControllers(directiveName, require, $element, elementControllers) { 20119 var value; 20120 20121 if (isString(require)) { 20122 var match = require.match(REQUIRE_PREFIX_REGEXP); 20123 var name = require.substring(match[0].length); 20124 var inheritType = match[1] || match[3]; 20125 var optional = match[2] === '?'; 20126 20127 //If only parents then start at the parent element 20128 if (inheritType === '^^') { 20129 $element = $element.parent(); 20130 //Otherwise attempt getting the controller from elementControllers in case 20131 //the element is transcluded (and has no data) and to avoid .data if possible 20132 } else { 20133 value = elementControllers && elementControllers[name]; 20134 value = value && value.instance; 20135 } 20136 20137 if (!value) { 20138 var dataName = '$' + name + 'Controller';
20139 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
vendor: 10,984 bytes, lines 20139-20406
20139 20140 } 20141 20142 if (!value && !optional) { 20143 throw $compileMinErr('ctreq', 20144 'Controller \'{0}\', required by directive \'{1}\', can\'t be found!', 20145 name, directiveName); 20146 } 20147 } else if (isArray(require)) { 20148 value = []; 20149 for (var i = 0, ii = require.length; i < ii; i++) { 20150 value[i] = getControllers(directiveName, require[i], $element, elementControllers); 20151 } 20152 } else if (isObject(require)) { 20153 value = {}; 20154 forEach(require, function(controller, property) { 20155 value[property] = getControllers(directiveName, controller, $element, elementControllers); 20156 }); 20157 } 20158 20159 return value || null; 20160 } 20161 20162 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) { 20163 var elementControllers = createMap(); 20164 for (var controllerKey in controllerDirectives) { 20165 var directive = controllerDirectives[controllerKey]; 20166 var locals = { 20167 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, 20168 $element: $element, 20169 $attrs: attrs, 20170 $transclude: transcludeFn 20171 }; 20172 20173 var controller = directive.controller; 20174 if (controller === '@') { 20175 controller = attrs[directive.name]; 20176 } 20177 20178 var controllerInstance = $controller(controller, locals, true, directive.controllerAs); 20179 20180 // For directives with element transclusion the element is a comment. 20181 // In this case .data will not attach any data. 20182 // Instead, we save the controllers for the element in a local hash and attach to .data 20183 // later, once we have the actual element. 20184 elementControllers[directive.name] = controllerInstance; 20185 $element.data('$' + directive.name + 'Controller', controllerInstance.instance); 20186 } 20187 return elementControllers; 20188 } 20189 20190 // Depending upon the context in which a directive finds itself it might need to have a new isolated 20191 // or child scope created. For instance: 20192 // * if the directive has been pulled into a template because another directive with a higher priority 20193 // asked for element transclusion 20194 // * if the directive itself asks for transclusion but it is at the root of a template and the original 20195 // element was replaced. See https://github.com/angular/angular.js/issues/12936 20196 function markDirectiveScope(directives, isolateScope, newScope) { 20197 for (var j = 0, jj = directives.length; j < jj; j++) { 20198 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope}); 20199 } 20200 } 20201 20202 /** 20203 * looks up the directive and decorates it with exception handling and proper parameters. We 20204 * call this the boundDirective. 20205 * 20206 * @param {string} name name of the directive to look up. 20207 * @param {string} location The directive must be found in specific format. 20208 * String containing any of theses characters: 20209 * 20210 * * `E`: element name 20211 * * `A': attribute 20212 * * `C`: class 20213 * * `M`: comment 20214 * @returns {boolean} true if directive was added. 20215 */ 20216 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, 20217 endAttrName) { 20218 if (name === ignoreDirective) return null; 20219 var match = null; 20220 if (hasDirectives.hasOwnProperty(name)) { 20221 for (var directive, directives = $injector.get(name + Suffix), 20222 i = 0, ii = directives.length; i < ii; i++) { 20223 directive = directives[i]; 20224 if ((isUndefined(maxPriority) || maxPriority > directive.priority) && 20225 directive.restrict.indexOf(location) !== -1) { 20226 if (startAttrName) { 20227 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); 20228 } 20229 if (!directive.$$bindings) { 20230 var bindings = directive.$$bindings = 20231 parseDirectiveBindings(directive, directive.name); 20232 if (isObject(bindings.isolateScope)) { 20233 directive.$$isolateBindings = bindings.isolateScope; 20234 } 20235 } 20236 tDirectives.push(directive); 20237 match = directive; 20238 } 20239 } 20240 } 20241 return match; 20242 } 20243 20244 20245 /** 20246 * looks up the directive and returns true if it is a multi-element directive, 20247 * and therefore requires DOM nodes between -start and -end markers to be grouped 20248 * together. 20249 * 20250 * @param {string} name name of the directive to look up. 20251 * @returns true if directive was registered as multi-element. 20252 */ 20253 function directiveIsMultiElement(name) { 20254 if (hasDirectives.hasOwnProperty(name)) { 20255 for (var directive, directives = $injector.get(name + Suffix), 20256 i = 0, ii = directives.length; i < ii; i++) { 20257 directive = directives[i]; 20258 if (directive.multiElement) { 20259 return true; 20260 } 20261 } 20262 } 20263 return false; 20264 } 20265 20266 /** 20267 * When the element is replaced with HTML template then the new attributes 20268 * on the template need to be merged with the existing attributes in the DOM. 20269 * The desired effect is to have both of the attributes present. 20270 * 20271 * @param {object} dst destination attributes (original DOM) 20272 * @param {object} src source attributes (from the directive template) 20273 */ 20274 function mergeTemplateAttributes(dst, src) { 20275 var srcAttr = src.$attr, 20276 dstAttr = dst.$attr; 20277 20278 // reapply the old attributes to the new element 20279 forEach(dst, function(value, key) { 20280 if (key.charAt(0) !== '$') { 20281 if (src[key] && src[key] !== value) { 20282 if (value.length) { 20283 value += (key === 'style' ? ';' : ' ') + src[key]; 20284 } else { 20285 value = src[key]; 20286 } 20287 } 20288 dst.$set(key, value, true, srcAttr[key]); 20289 } 20290 }); 20291 20292 // copy the new attributes on the old attrs object 20293 forEach(src, function(value, key) { 20294 // Check if we already set this attribute in the loop above. 20295 // `dst` will never contain hasOwnProperty as DOM parser won't let it. 20296 // You will get an "InvalidCharacterError: DOM Exception 5" error if you 20297 // have an attribute like "has-own-property" or "data-has-own-property", etc. 20298 if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') { 20299 dst[key] = value; 20300 20301 if (key !== 'class' && key !== 'style') { 20302 dstAttr[key] = srcAttr[key]; 20303 } 20304 } 20305 }); 20306 } 20307 20308 20309 function compileTemplateUrl(directives, $compileNode, tAttrs, 20310 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { 20311 var linkQueue = [], 20312 afterTemplateNodeLinkFn, 20313 afterTemplateChildLinkFn, 20314 beforeTemplateCompileNode = $compileNode[0], 20315 origAsyncDirective = directives.shift(), 20316 derivedSyncDirective = inherit(origAsyncDirective, { 20317 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective 20318 }), 20319 templateUrl = (isFunction(origAsyncDirective.templateUrl)) 20320 ? origAsyncDirective.templateUrl($compileNode, tAttrs) 20321 : origAsyncDirective.templateUrl, 20322 templateNamespace = origAsyncDirective.templateNamespace; 20323 20324 $compileNode.empty(); 20325 20326 $templateRequest(templateUrl) 20327 .then(function(content) { 20328 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; 20329 20330 content = denormalizeTemplate(content); 20331 20332 if (origAsyncDirective.replace) { 20333 if (jqLiteIsTextNode(content)) { 20334 $template = []; 20335 } else { 20336 $template = removeComments(wrapTemplate(templateNamespace, trim(content))); 20337 } 20338 compileNode = $template[0]; 20339 20340 if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { 20341 throw $compileMinErr('tplrt', 20342 'Template for directive \'{0}\' must have exactly one root element. {1}', 20343 origAsyncDirective.name, templateUrl); 20344 } 20345 20346 tempTemplateAttrs = {$attr: {}}; 20347 replaceWith($rootElement, $compileNode, compileNode); 20348 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); 20349 20350 if (isObject(origAsyncDirective.scope)) { 20351 // the original directive that caused the template to be loaded async required 20352 // an isolate scope 20353 markDirectiveScope(templateDirectives, true); 20354 } 20355 directives = templateDirectives.concat(directives); 20356 mergeTemplateAttributes(tAttrs, tempTemplateAttrs); 20357 } else { 20358 compileNode = beforeTemplateCompileNode; 20359 $compileNode.html(content); 20360 } 20361 20362 directives.unshift(derivedSyncDirective); 20363 20364 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, 20365 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, 20366 previousCompileContext); 20367 forEach($rootElement, function(node, i) { 20368 if (node === compileNode) { 20369 $rootElement[i] = $compileNode[0]; 20370 } 20371 }); 20372 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); 20373 20374 while (linkQueue.length) { 20375 var scope = linkQueue.shift(), 20376 beforeTemplateLinkNode = linkQueue.shift(), 20377 linkRootElement = linkQueue.shift(), 20378 boundTranscludeFn = linkQueue.shift(), 20379 linkNode = $compileNode[0]; 20380 20381 if (scope.$$destroyed) continue; 20382 20383 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { 20384 var oldClasses = beforeTemplateLinkNode.className; 20385 20386 if (!(previousCompileContext.hasElementTranscludeDirective && 20387 origAsyncDirective.replace)) { 20388 // it was cloned therefore we have to clone as well. 20389 linkNode = jqLiteClone(compileNode); 20390 } 20391 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); 20392 20393 // Copy in CSS classes from original node 20394 safeAddClass(jqLite(linkNode), oldClasses); 20395 } 20396 if (afterTemplateNodeLinkFn.transcludeOnThisElement) { 20397 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); 20398 } else { 20399 childBoundTranscludeFn = boundTranscludeFn; 20400 } 20401 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, 20402 childBoundTranscludeFn); 20403 } 20404 linkQueue = null; 20405 }).catch(function(error) { 20406 if (
vendor: 28,064 bytes, lines 20406-21163
20406error instanceof Error) { 20407 $exceptionHandler(error); 20408 } 20409 }); 20410 20411 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { 20412 var childBoundTranscludeFn = boundTranscludeFn; 20413 if (scope.$$destroyed) return; 20414 if (linkQueue) { 20415 linkQueue.push(scope, 20416 node, 20417 rootElement, 20418 childBoundTranscludeFn); 20419 } else { 20420 if (afterTemplateNodeLinkFn.transcludeOnThisElement) { 20421 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); 20422 } 20423 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); 20424 } 20425 }; 20426 } 20427 20428 20429 /** 20430 * Sorting function for bound directives. 20431 */ 20432 function byPriority(a, b) { 20433 var diff = b.priority - a.priority; 20434 if (diff !== 0) return diff; 20435 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; 20436 return a.index - b.index; 20437 } 20438 20439 function assertNoDuplicate(what, previousDirective, directive, element) { 20440 20441 function wrapModuleNameIfDefined(moduleName) { 20442 return moduleName ? 20443 (' (module: ' + moduleName + ')') : 20444 ''; 20445 } 20446 20447 if (previousDirective) { 20448 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}', 20449 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName), 20450 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element)); 20451 } 20452 } 20453 20454 20455 function addTextInterpolateDirective(directives, text) { 20456 var interpolateFn = $interpolate(text, true); 20457 if (interpolateFn) { 20458 directives.push({ 20459 priority: 0, 20460 compile: function textInterpolateCompileFn(templateNode) { 20461 var templateNodeParent = templateNode.parent(), 20462 hasCompileParent = !!templateNodeParent.length; 20463 20464 // When transcluding a template that has bindings in the root 20465 // we don't have a parent and thus need to add the class during linking fn. 20466 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent); 20467 20468 return function textInterpolateLinkFn(scope, node) { 20469 var parent = node.parent(); 20470 if (!hasCompileParent) compile.$$addBindingClass(parent); 20471 compile.$$addBindingInfo(parent, interpolateFn.expressions); 20472 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { 20473 node[0].nodeValue = value; 20474 }); 20475 }; 20476 } 20477 }); 20478 } 20479 } 20480 20481 20482 function wrapTemplate(type, template) { 20483 type = lowercase(type || 'html'); 20484 switch (type) { 20485 case 'svg': 20486 case 'math': 20487 var wrapper = window.document.createElement('div'); 20488 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>'; 20489 return wrapper.childNodes[0].childNodes; 20490 default: 20491 return template; 20492 } 20493 } 20494 20495 20496 function getTrustedContext(node, attrNormalizedName) { 20497 if (attrNormalizedName === 'srcdoc') { 20498 return $sce.HTML; 20499 } 20500 var tag = nodeName_(node); 20501 // All tags with src attributes require a RESOURCE_URL value, except for 20502 // img and various html5 media tags. 20503 if (attrNormalizedName === 'src' || attrNormalizedName === 'ngSrc') { 20504 if (['img', 'video', 'audio', 'source', 'track'].indexOf(tag) === -1) { 20505 return $sce.RESOURCE_URL; 20506 } 20507 // maction[xlink:href] can source SVG. It's not limited to <maction>. 20508 } else if (attrNormalizedName === 'xlinkHref' || 20509 (tag === 'form' && attrNormalizedName === 'action') || 20510 // links can be stylesheets or imports, which can run script in the current origin 20511 (tag === 'link' && attrNormalizedName === 'href') 20512 ) { 20513 return $sce.RESOURCE_URL; 20514 } 20515 } 20516 20517 20518 function addAttrInterpolateDirective(node, directives, value, name, isNgAttr) { 20519 var trustedContext = getTrustedContext(node, name); 20520 var mustHaveExpression = !isNgAttr; 20521 var allOrNothing = ALL_OR_NOTHING_ATTRS[name] || isNgAttr; 20522 20523 var interpolateFn = $interpolate(value, mustHaveExpression, trustedContext, allOrNothing); 20524 20525 // no interpolation found -> ignore 20526 if (!interpolateFn) return; 20527 20528 if (name === 'multiple' && nodeName_(node) === 'select') { 20529 throw $compileMinErr('selmulti', 20530 'Binding to the \'multiple\' attribute is not supported. Element: {0}', 20531 startingTag(node)); 20532 } 20533 20534 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { 20535 throw $compileMinErr('nodomevents', 20536 'Interpolations for HTML DOM event attributes are disallowed. Please use the ' + 20537 'ng- versions (such as ng-click instead of onclick) instead.'); 20538 } 20539 20540 directives.push({ 20541 priority: 100, 20542 compile: function() { 20543 return { 20544 pre: function attrInterpolatePreLinkFn(scope, element, attr) { 20545 var $$observers = (attr.$$observers || (attr.$$observers = createMap())); 20546 20547 // If the attribute has changed since last $interpolate()ed 20548 var newValue = attr[name]; 20549 if (newValue !== value) { 20550 // we need to interpolate again since the attribute value has been updated 20551 // (e.g. by another directive's compile function) 20552 // ensure unset/empty values make interpolateFn falsy 20553 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing); 20554 value = newValue; 20555 } 20556 20557 // if attribute was updated so that there is no interpolation going on we don't want to 20558 // register any observers 20559 if (!interpolateFn) return; 20560 20561 // initialize attr object so that it's ready in case we need the value for isolate 20562 // scope initialization, otherwise the value would not be available from isolate 20563 // directive's linking fn during linking phase 20564 attr[name] = interpolateFn(scope); 20565 20566 ($$observers[name] || ($$observers[name] = [])).$$inter = true; 20567 (attr.$$observers && attr.$$observers[name].$$scope || scope). 20568 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { 20569 //special case for class attribute addition + removal 20570 //so that class changes can tap into the animation 20571 //hooks provided by the $animate service. Be sure to 20572 //skip animations when the first digest occurs (when 20573 //both the new and the old values are the same) since 20574 //the CSS classes are the non-interpolated values 20575 if (name === 'class' && newValue !== oldValue) { 20576 attr.$updateClass(newValue, oldValue); 20577 } else { 20578 attr.$set(name, newValue); 20579 } 20580 }); 20581 } 20582 }; 20583 } 20584 }); 20585 } 20586 20587 20588 /** 20589 * This is a special jqLite.replaceWith, which can replace items which 20590 * have no parents, provided that the containing jqLite collection is provided. 20591 * 20592 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes 20593 * in the root of the tree. 20594 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep 20595 * the shell, but replace its DOM node reference. 20596 * @param {Node} newNode The new DOM node. 20597 */ 20598 function replaceWith($rootElement, elementsToRemove, newNode) { 20599 var firstElementToRemove = elementsToRemove[0], 20600 removeCount = elementsToRemove.length, 20601 parent = firstElementToRemove.parentNode, 20602 i, ii; 20603 20604 if ($rootElement) { 20605 for (i = 0, ii = $rootElement.length; i < ii; i++) { 20606 if ($rootElement[i] === firstElementToRemove) { 20607 $rootElement[i++] = newNode; 20608 for (var j = i, j2 = j + removeCount - 1, 20609 jj = $rootElement.length; 20610 j < jj; j++, j2++) { 20611 if (j2 < jj) { 20612 $rootElement[j] = $rootElement[j2]; 20613 } else { 20614 delete $rootElement[j]; 20615 } 20616 } 20617 $rootElement.length -= removeCount - 1; 20618 20619 // If the replaced element is also the jQuery .context then replace it 20620 // .context is a deprecated jQuery api, so we should set it only when jQuery set it 20621 // http://api.jquery.com/context/ 20622 if ($rootElement.context === firstElementToRemove) { 20623 $rootElement.context = newNode; 20624 } 20625 break; 20626 } 20627 } 20628 } 20629 20630 if (parent) { 20631 parent.replaceChild(newNode, firstElementToRemove); 20632 } 20633 20634 // Append all the `elementsToRemove` to a fragment. This will... 20635 // - remove them from the DOM 20636 // - allow them to still be traversed with .nextSibling 20637 // - allow a single fragment.qSA to fetch all elements being removed 20638 var fragment = window.document.createDocumentFragment(); 20639 for (i = 0; i < removeCount; i++) { 20640 fragment.appendChild(elementsToRemove[i]); 20641 } 20642 20643 if (jqLite.hasData(firstElementToRemove)) { 20644 // Copy over user data (that includes Angular's $scope etc.). Don't copy private 20645 // data here because there's no public interface in jQuery to do that and copying over 20646 // event listeners (which is the main use of private data) wouldn't work anyway. 20647 jqLite.data(newNode, jqLite.data(firstElementToRemove)); 20648 20649 // Remove $destroy event listeners from `firstElementToRemove` 20650 jqLite(firstElementToRemove).off('$destroy'); 20651 } 20652 20653 // Cleanup any data/listeners on the elements and children. 20654 // This includes invoking the $destroy event on any elements with listeners. 20655 jqLite.cleanData(fragment.querySelectorAll('*')); 20656 20657 // Update the jqLite collection to only contain the `newNode` 20658 for (i = 1; i < removeCount; i++) { 20659 delete elementsToRemove[i]; 20660 } 20661 elementsToRemove[0] = newNode; 20662 elementsToRemove.length = 1; 20663 } 20664 20665 20666 function cloneAndAnnotateFn(fn, annotation) { 20667 return extend(function() { return fn.apply(null, arguments); }, fn, annotation); 20668 } 20669 20670 20671 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { 20672 try { 20673 linkFn(scope, $element, attrs, controllers, transcludeFn); 20674 } catch (e) { 20675 $exceptionHandler(e, startingTag($element)); 20676 } 20677 } 20678 20679 20680 // Set up $watches for isolate scope and controller bindings. 20681 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) { 20682 var removeWatchCollection = []; 20683 var initialChanges = {}; 20684 var changes; 20685 forEach(bindings, function initializeBinding(definition, scopeName) { 20686 var attrName = definition.attrName, 20687 optional = definition.optional, 20688 mode = definition.mode, // @, =, <, or & 20689 lastValue, 20690 parentGet, parentSet, compare, removeWatch; 20691 20692 switch (mode) { 20693 20694 case '@': 20695 if (!optional && !hasOwnProperty.call(attrs, attrName)) { 20696 destination[scopeName] = attrs[attrName] = undefined; 20697 } 20698 removeWatch = attrs.$observe(attrName, function(value) { 20699 if (isString(value) || isBoolean(value)) { 20700 var oldValue = destination[scopeName]; 20701 recordChanges(scopeName, value, oldValue); 20702 destination[scopeName] = value; 20703 } 20704 }); 20705 attrs.$$observers[attrName].$$scope = scope; 20706 lastValue = attrs[attrName]; 20707 if (isString(lastValue)) { 20708 // If the attribute has been provided then we trigger an interpolation to ensure 20709 // the value is there for use in the link fn 20710 destination[scopeName] = $interpolate(lastValue)(scope); 20711 } else if (isBoolean(lastValue)) { 20712 // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted 20713 // the value to boolean rather than a string, so we special case this situation 20714 destination[scopeName] = lastValue; 20715 } 20716 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]); 20717 removeWatchCollection.push(removeWatch); 20718 break; 20719 20720 case '=': 20721 if (!hasOwnProperty.call(attrs, attrName)) { 20722 if (optional) break; 20723 attrs[attrName] = undefined; 20724 } 20725 if (optional && !attrs[attrName]) break; 20726 20727 parentGet = $parse(attrs[attrName]); 20728 if (parentGet.literal) { 20729 compare = equals; 20730 } else { 20731 // eslint-disable-next-line no-self-compare 20732 compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }; 20733 } 20734 parentSet = parentGet.assign || function() { 20735 // reset the change, or we will throw this exception on every $digest 20736 lastValue = destination[scopeName] = parentGet(scope); 20737 throw $compileMinErr('nonassign', 20738 'Expression \'{0}\' in attribute \'{1}\' used with directive \'{2}\' is non-assignable!', 20739 attrs[attrName], attrName, directive.name); 20740 }; 20741 lastValue = destination[scopeName] = parentGet(scope); 20742 var parentValueWatch = function parentValueWatch(parentValue) { 20743 if (!compare(parentValue, destination[scopeName])) { 20744 // we are out of sync and need to copy 20745 if (!compare(parentValue, lastValue)) { 20746 // parent changed and it has precedence 20747 destination[scopeName] = parentValue; 20748 } else { 20749 // if the parent can be assigned then do so 20750 parentSet(scope, parentValue = destination[scopeName]); 20751 } 20752 } 20753 lastValue = parentValue; 20754 return lastValue; 20755 }; 20756 parentValueWatch.$stateful = true; 20757 if (definition.collection) { 20758 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch); 20759 } else { 20760 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); 20761 } 20762 removeWatchCollection.push(removeWatch); 20763 break; 20764 20765 case '<': 20766 if (!hasOwnProperty.call(attrs, attrName)) { 20767 if (optional) break; 20768 attrs[attrName] = undefined; 20769 } 20770 if (optional && !attrs[attrName]) break; 20771 20772 parentGet = $parse(attrs[attrName]); 20773 var deepWatch = parentGet.literal; 20774 20775 var initialValue = destination[scopeName] = parentGet(scope); 20776 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]); 20777 20778 removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) { 20779 if (oldValue === newValue) { 20780 if (oldValue === initialValue || (deepWatch && equals(oldValue, initialValue))) { 20781 return; 20782 } 20783 oldValue = initialValue; 20784 } 20785 recordChanges(scopeName, newValue, oldValue); 20786 destination[scopeName] = newValue; 20787 }, deepWatch); 20788 20789 removeWatchCollection.push(removeWatch); 20790 break; 20791 20792 case '&': 20793 // Don't assign Object.prototype method to scope 20794 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop; 20795 20796 // Don't assign noop to destination if expression is not valid 20797 if (parentGet === noop && optional) break; 20798 20799 destination[scopeName] = function(locals) { 20800 return parentGet(scope, locals); 20801 }; 20802 break; 20803 } 20804 }); 20805 20806 function recordChanges(key, currentValue, previousValue) { 20807 if (isFunction(destination.$onChanges) && currentValue !== previousValue && 20808 // eslint-disable-next-line no-self-compare 20809 (currentValue === currentValue || previousValue === previousValue)) { 20810 // If we have not already scheduled the top level onChangesQueue handler then do so now 20811 if (!onChangesQueue) { 20812 scope.$$postDigest(flushOnChangesQueue); 20813 onChangesQueue = []; 20814 } 20815 // If we have not already queued a trigger of onChanges for this controller then do so now 20816 if (!changes) { 20817 changes = {}; 20818 onChangesQueue.push(triggerOnChangesHook); 20819 } 20820 // If the has been a change on this property already then we need to reuse the previous value 20821 if (changes[key]) { 20822 previousValue = changes[key].previousValue; 20823 } 20824 // Store this change 20825 changes[key] = new SimpleChange(previousValue, currentValue); 20826 } 20827 } 20828 20829 function triggerOnChangesHook() { 20830 destination.$onChanges(changes); 20831 // Now clear the changes so that we schedule onChanges when more changes arrive 20832 changes = undefined; 20833 } 20834 20835 return { 20836 initialChanges: initialChanges, 20837 removeWatches: removeWatchCollection.length && function removeWatches() { 20838 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) { 20839 removeWatchCollection[i](); 20840 } 20841 } 20842 }; 20843 } 20844 }]; 20845} 20846 20847function SimpleChange(previous, current) { 20848 this.previousValue = previous; 20849 this.currentValue = current; 20850} 20851SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; }; 20852 20853 20854var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i; 20855var SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g; 20856 20857/** 20858 * Converts all accepted directives format into proper directive name. 20859 * @param name Name to normalize 20860 */ 20861function directiveNormalize(name) { 20862 return name 20863 .replace(PREFIX_REGEXP, '') 20864 .replace(SPECIAL_CHARS_REGEXP, fnCamelCaseReplace); 20865} 20866 20867/** 20868 * @ngdoc type 20869 * @name $compile.directive.Attributes 20870 * 20871 * @description 20872 * A shared object between directive compile / linking functions which contains normalized DOM 20873 * element attributes. The values reflect current binding state `{{ }}`. The normalization is 20874 * needed since all of these are treated as equivalent in Angular: 20875 * 20876 * ``` 20877 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a"> 20878 * ``` 20879 */ 20880 20881/** 20882 * @ngdoc property 20883 * @name $compile.directive.Attributes#$attr 20884 * 20885 * @description 20886 * A map of DOM element attribute names to the normalized name. This is 20887 * needed to do reverse lookup from normalized name back to actual name. 20888 */ 20889 20890 20891/** 20892 * @ngdoc method 20893 * @name $compile.directive.Attributes#$set 20894 * @kind function 20895 * 20896 * @description 20897 * Set DOM element attribute value. 20898 * 20899 * 20900 * @param {string} name Normalized element attribute name of the property to modify. The name is 20901 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} 20902 * property to the original name. 20903 * @param {string} value Value to set the attribute to. The value can be an interpolated string. 20904 */ 20905 20906 20907 20908/** 20909 * Closure compiler type information 20910 */ 20911 20912function nodesetLinkingFn( 20913 /* angular.Scope */ scope, 20914 /* NodeList */ nodeList, 20915 /* Element */ rootElement, 20916 /* function(Function) */ boundTranscludeFn 20917) {} 20918 20919function directiveLinkingFn( 20920 /* nodesetLinkingFn */ nodesetLinkingFn, 20921 /* angular.Scope */ scope, 20922 /* Node */ node, 20923 /* Element */ rootElement, 20924 /* function(Function) */ boundTranscludeFn 20925) {} 20926 20927function tokenDifference(str1, str2) { 20928 var values = '', 20929 tokens1 = str1.split(/\s+/), 20930 tokens2 = str2.split(/\s+/); 20931 20932 outer: 20933 for (var i = 0; i < tokens1.length; i++) { 20934 var token = tokens1[i]; 20935 for (var j = 0; j < tokens2.length; j++) { 20936 if (token === tokens2[j]) continue outer; 20937 } 20938 values += (values.length > 0 ? ' ' : '') + token; 20939 } 20940 return values; 20941} 20942 20943function removeComments(jqNodes) { 20944 jqNodes = jqLite(jqNodes); 20945 var i = jqNodes.length; 20946 20947 if (i <= 1) { 20948 return jqNodes; 20949 } 20950 20951 while (i--) { 20952 var node = jqNodes[i]; 20953 if (node.nodeType === NODE_TYPE_COMMENT || 20954 (node.nodeType === NODE_TYPE_TEXT && node.nodeValue.trim() === '')) { 20955 splice.call(jqNodes, i, 1); 20956 } 20957 } 20958 return jqNodes; 20959} 20960 20961var $controllerMinErr = minErr('$controller'); 20962 20963 20964var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/; 20965function identifierForController(controller, ident) { 20966 if (ident && isString(ident)) return ident; 20967 if (isString(controller)) { 20968 var match = CNTRL_REG.exec(controller); 20969 if (match) return match[3]; 20970 } 20971} 20972 20973 20974/** 20975 * @ngdoc provider 20976 * @name $controllerProvider 20977 * @this 20978 * 20979 * @description 20980 * The {@link ng.$controller $controller service} is used by Angular to create new 20981 * controllers. 20982 * 20983 * This provider allows controller registration via the 20984 * {@link ng.$controllerProvider#register register} method. 20985 */ 20986function $ControllerProvider() { 20987 var controllers = {}, 20988 globals = false; 20989 20990 /** 20991 * @ngdoc method 20992 * @name $controllerProvider#has 20993 * @param {string} name Controller name to check. 20994 */ 20995 this.has = function(name) { 20996 return controllers.hasOwnProperty(name); 20997 }; 20998 20999 /** 21000 * @ngdoc method 21001 * @name $controllerProvider#register 21002 * @param {string|Object} name Controller name, or an object map of controllers where the keys are 21003 * the names and the values are the constructors. 21004 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI 21005 * annotations in the array notation). 21006 */ 21007 this.register = function(name, constructor) { 21008 assertNotHasOwnProperty(name, 'controller'); 21009 if (isObject(name)) { 21010 extend(controllers, name); 21011 } else { 21012 controllers[name] = constructor; 21013 } 21014 }; 21015 21016 /** 21017 * @ngdoc method 21018 * @name $controllerProvider#allowGlobals 21019 * @description If called, allows `$controller` to find controller constructors on `window` 21020 * 21021 * @deprecated 21022 * sinceVersion="v1.3.0" 21023 * removeVersion="v1.7.0" 21024 * This method of finding controllers has been deprecated. 21025 */ 21026 this.allowGlobals = function() { 21027 globals = true; 21028 }; 21029 21030 21031 this.$get = ['$injector', '$window', function($injector, $window) { 21032 21033 /** 21034 * @ngdoc service 21035 * @name $controller 21036 * @requires $injector 21037 * 21038 * @param {Function|string} constructor If called with a function then it's considered to be the 21039 * controller constructor function. Otherwise it's considered to be a string which is used 21040 * to retrieve the controller constructor using the following steps: 21041 * 21042 * * check if a controller with given name is registered via `$controllerProvider` 21043 * * check if evaluating the string on the current scope returns a constructor 21044 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global 21045 * `window` object (deprecated, not recommended) 21046 * 21047 * The string can use the `controller as property` syntax, where the controller instance is published 21048 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this 21049 * to work correctly. 21050 * 21051 * @param {Object} locals Injection locals for Controller. 21052 * @return {Object} Instance of given controller. 21053 * 21054 * @description 21055 * `$controller` service is responsible for instantiating controllers. 21056 * 21057 * It's just a simple call to {@link auto.$injector $injector}, but extracted into 21058 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). 21059 */ 21060 return function $controller(expression, locals, later, ident) { 21061 // PRIVATE API: 21062 // param `later` --- indicates that the controller's constructor is invoked at a later time. 21063 // If true, $controller will allocate the object with the correct 21064 // prototype chain, but will not invoke the controller until a returned 21065 // callback is invoked. 21066 // param `ident` --- An optional label which overrides the label parsed from the controller 21067 // expression, if any. 21068 var instance, match, constructor, identifier; 21069 later = later === true; 21070 if (ident && isString(ident)) { 21071 identifier = ident; 21072 } 21073 21074 if (isString(expression)) { 21075 match = expression.match(CNTRL_REG); 21076 if (!match) { 21077 throw $controllerMinErr('ctrlfmt', 21078 'Badly formed controller string \'{0}\'. ' + 21079 'Must match `__name__ as __id__` or `__name__`.', expression); 21080 } 21081 constructor = match[1]; 21082 identifier = identifier || match[3]; 21083 expression = controllers.hasOwnProperty(constructor) 21084 ? controllers[constructor] 21085 : getter(locals.$scope, constructor, true) || 21086 (globals ? getter($window, constructor, true) : undefined); 21087 21088 if (!expression) { 21089 throw $controllerMinErr('ctrlreg', 21090 'The controller with the name \'{0}\' is not registered.', constructor); 21091 } 21092 21093 assertArgFn(expression, constructor, true); 21094 } 21095 21096 if (later) { 21097 // Instantiate controller later: 21098 // This machinery is used to create an instance of the object before calling the 21099 // controller's constructor itself. 21100 // 21101 // This allows properties to be added to the controller before the constructor is 21102 // invoked. Primarily, this is used for isolate scope bindings in $compile. 21103 // 21104 // This feature is not intended for use by applications, and is thus not documented 21105 // publicly. 21106 // Object creation: http://jsperf.com/create-constructor/2 21107 var controllerPrototype = (isArray(expression) ? 21108 expression[expression.length - 1] : expression).prototype; 21109 instance = Object.create(controllerPrototype || null); 21110 21111 if (identifier) { 21112 addIdentifier(locals, identifier, instance, constructor || expression.name); 21113 } 21114 21115 return extend(function $controllerInit() { 21116 var result = $injector.invoke(expression, instance, locals, constructor); 21117 if (result !== instance && (isObject(result) || isFunction(result))) { 21118 instance = result; 21119 if (identifier) { 21120 // If result changed, re-assign controllerAs value to scope. 21121 addIdentifier(locals, identifier, instance, constructor || expression.name); 21122 } 21123 } 21124 return instance; 21125 }, { 21126 instance: instance, 21127 identifier: identifier 21128 }); 21129 } 21130 21131 instance = $injector.instantiate(expression, locals, constructor); 21132 21133 if (identifier) { 21134 addIdentifier(locals, identifier, instance, constructor || expression.name); 21135 } 21136 21137 return instance; 21138 }; 21139 21140 function addIdentifier(locals, identifier, instance, name) { 21141 if (!(locals && isObject(locals.$scope))) { 21142 throw minErr('$controller')('noscp', 21143 'Cannot export controller \'{0}\' as \'{1}\'! No $scope object provided via `locals`.', 21144 name, identifier); 21145 } 21146 21147 locals.$scope[identifier] = instance; 21148 } 21149 }]; 21150} 21151 21152/** 21153 * @ngdoc service 21154 * @name $document 21155 * @requires $window 21156 * @this 21157 * 21158 * @description 21159 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. 21160 * 21161 * @example 21162 <example module="documentExample" name="document"> 21163 <file name="index.html">
21164 <div ng-controller="ExampleController"> 21165 <p>$document title: <b ng-bind="title"></b></p> 21166 <p>window.document title: <b ng-bind="windowTitle"></b></p> 21167 </div> 21168 </file> 21169 <file name="script.js"> 21170 angular.module('documentExample', []) 21171 .controller('ExampleController', ['$scope', '$document', function($scope, $document) { 21172 $scope.title = $document[0].title; 21173 $scope.windowTitle = angular.element(window.document)[0].title; 21174 }]); 21175 </file> 21176 </example> 21177 */ 21178function $DocumentProvider() { 21179 this.$get = ['$window', function(window) { 21180 return jqLite(window.document); 21181 }]; 21182} 21183 21184 21185/** 21186 * @private 21187 * @this 21188 * Listens for document visibility change and makes the current status accessible. 21189 */ 21190function $$IsDocumentHiddenProvider() { 21191 this.$get = ['$document', '$rootScope', function($document, $rootScope) { 21192 var doc = $document[0]; 21193 var hidden = doc && doc.hidden; 21194 21195 $document.on('visibilitychange', changeListener); 21196 21197 $rootScope.$on('$destroy', function() { 21198 $document.off('visibilitychange', changeListener); 21199 }); 21200 21201 function changeListener() { 21202 hidden = doc.hidden; 21203 } 21204 21205 return function() { 21206 return hidden; 21207 }; 21208 }]; 21209} 21210 21211/** 21212 * @ngdoc service 21213 * @name $exceptionHandler 21214 * @requires ng.$log 21215 * @this 21216 * 21217 * @description 21218 * Any uncaught exception in angular expressions is delegated to this service. 21219 * The default implementation simply delegates to `$log.error` which logs it into 21220 * the browser console. 21221 * 21222 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by 21223 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. 21224 * 21225 * ## Example: 21226 * 21227 * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught 21228 * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead 21229 * of `$log.error()`. 21230 * 21231 * ```js 21232 * angular. 21233 * module('exceptionOverwrite', []). 21234 * factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) { 21235 * return function myExceptionHandler(exception, cause) { 21236 * logErrorsToBackend(exception, cause); 21237 * $log.warn(exception, cause); 21238 * }; 21239 * }
21239]); 21240 * ``` 21241 * 21242 * <hr /> 21243 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind` 21244 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler} 21245 * (unless executed during a digest). 21246 * 21247 * If you wish, you can manually delegate exceptions, e.g. 21248 * `try { ... } catch(e) { $exceptionHandler(e); }` 21249 * 21250 * @param {Error} exception Exception associated with the error. 21251 * @param {string=} cause Optional information about the context in which 21252 * the error was thrown. 21253 * 21254 */ 21255function $ExceptionHandlerProvider() { 21256 this.$get = ['$log', function($log) { 21257 return function(exception, cause) { 21258 $log.error.apply($log, arguments); 21259 }; 21260 }]; 21261} 21262 21263var $$ForceReflowProvider = /** @this */ function() { 21264 this.$get = ['$document', function($document) { 21265 return function(domNode) { 21266 //the line below will force the browser to perform a repaint so 21267 //that all the animated elements within the animation frame will 21268 //be properly updated and drawn on screen. This is required to 21269 //ensure that the preparation animation is properly flushed so that 21270 //the active state picks up from there. DO NOT REMOVE THIS LINE. 21271 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH 21272 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND 21273 //WILL TAKE YEARS AWAY FROM YOUR LIFE. 21274 if (domNode) { 21275 if (!domNode.nodeType && domNode instanceof jqLite) { 21276 domNode = domNode[0]; 21277 } 21278 } else { 21279 domNode = $document[0].body; 21280 } 21281 return domNode.offsetWidth + 1; 21282 }; 21283 }]; 21284}; 21285 21286var APPLICATION_JSON = 'application/json'; 21287var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; 21288var JSON_START = /^\[|^\{(?!\{)/; 21289var JSON_ENDS = { 21290 '[': /]$/, 21291 '{': /}$/ 21292}; 21293var JSON_PROTECTION_PREFIX = /^\)]\}',?\n/; 21294var $httpMinErr = minErr('$http'); 21295 21296function serializeValue(v) { 21297 if (isObject(v)) { 21298 return isDate(v) ? v.toISOString() : toJson(v); 21299 } 21300 return v; 21301} 21302 21303 21304/** @this */ 21305function $HttpParamSerializerProvider() { 21306 /** 21307 * @ngdoc service 21308 * @name $httpParamSerializer 21309 * @description 21310 * 21311 * Default {@link $http `$http`} params serializer that converts objects to strings 21312 * according to the following rules: 21313 * 21314 * * `{'foo': 'bar'}` results in `foo=bar` 21315 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object) 21316 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element) 21317 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object) 21318 * 21319 * Note that serializer will sort the request parameters alphabetically. 21320 * */ 21321 21322 this.$get = function() { 21323 return function ngParamSerializer(params) { 21324 if (!params) return ''; 21325 var parts = []; 21326 forEachSorted(params, function(value, key) { 21327 if (value === null || isUndefined(value)) return; 21328 if (isArray(value)) { 21329 forEach(value, function(v) { 21330 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v))); 21331 }); 21332 } else { 21333 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value))); 21334 } 21335 }); 21336 21337 return parts.join('&'); 21338 }; 21339 }; 21340} 21341 21342/** @this */ 21343function $HttpParamSerializerJQLikeProvider() { 21344 /** 21345 * @ngdoc service 21346 * @name $httpParamSerializerJQLike 21347 * 21348 * @description 21349 * 21350 * Alternative {@link $http `$http`} params serializer that follows 21351 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic. 21352 * The serializer will also sort the params alphabetically. 21353 * 21354 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property: 21355 * 21356 * ```js 21357 * $http({ 21358 * url: myUrl, 21359 * method: 'GET', 21360 * params: myParams, 21361 * paramSerializer: '$httpParamSerializerJQLike' 21362 * }); 21363 * ``` 21364 * 21365 * It is also possible to set it as the default `paramSerializer` in the 21366 * {@link $httpProvider#defaults `$httpProvider`}. 21367 * 21368 * Additionally, you can inject the serializer and use it explicitly, for example to serialize 21369 * form data for submission: 21370 * 21371 * ```js 21372 * .controller(function($http, $httpParamSerializerJQLike) { 21373 * //... 21374 * 21375 * $http({ 21376 * url: myUrl, 21377 * method: 'POST', 21378 * data: $httpParamSerializerJQLike(myData), 21379 * headers: { 21380 * 'Content-Type': 'application/x-www-form-urlencoded' 21381 * } 21382 * }); 21383 * 21384 * }); 21385 * ``` 21386 * 21387 * */ 21388 this.$get = function() { 21389 return function jQueryLikeParamSerializer(params) { 21390 if (!params) return ''; 21391 var parts = []; 21392 serialize(params, '', true); 21393 return parts.join('&'); 21394 21395 function serialize(toSerialize, prefix, topLevel) { 21396 if (toSerialize === null || isUndefined(toSerialize)) return; 21397 if (isArray(toSerialize)) {
vendor: 6,108 bytes, lines 21398-21599
21398 forEach(toSerialize, function(value, index) { 21399 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']'); 21400 }); 21401 } else if (isObject(toSerialize) && !isDate(toSerialize)) { 21402 forEachSorted(toSerialize, function(value, key) { 21403 serialize(value, prefix + 21404 (topLevel ? '' : '[') + 21405 key + 21406 (topLevel ? '' : ']')); 21407 }); 21408 } else { 21409 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize))); 21410 } 21411 } 21412 }; 21413 }; 21414} 21415 21416function defaultHttpResponseTransform(data, headers) { 21417 if (isString(data)) { 21418 // Strip json vulnerability protection prefix and trim whitespace 21419 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim(); 21420 21421 if (tempData) { 21422 var contentType = headers('Content-Type'); 21423 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) { 21424 data = fromJson(tempData); 21425 } 21426 } 21427 } 21428 21429 return data; 21430} 21431 21432function isJsonLike(str) { 21433 var jsonStart = str.match(JSON_START); 21434 return jsonStart && JSON_ENDS[jsonStart[0]].test(str); 21435} 21436 21437/** 21438 * Parse headers into key value object 21439 * 21440 * @param {string} headers Raw headers as a string 21441 * @returns {Object} Parsed headers as key value object 21442 */ 21443function parseHeaders(headers) { 21444 var parsed = createMap(), i; 21445 21446 function fillInParsed(key, val) { 21447 if (key) { 21448 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 21449 } 21450 } 21451 21452 if (isString(headers)) { 21453 forEach(headers.split('\n'), function(line) { 21454 i = line.indexOf(':'); 21455 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1))); 21456 }); 21457 } else if (isObject(headers)) { 21458 forEach(headers, function(headerVal, headerKey) { 21459 fillInParsed(lowercase(headerKey), trim(headerVal)); 21460 }); 21461 } 21462 21463 return parsed; 21464} 21465 21466 21467/** 21468 * Returns a function that provides access to parsed headers. 21469 * 21470 * Headers are lazy parsed when first requested. 21471 * @see parseHeaders 21472 * 21473 * @param {(string|Object)} headers Headers to provide access to. 21474 * @returns {function(string=)} Returns a getter function which if called with: 21475 * 21476 * - if called with an argument returns a single header value or null 21477 * - if called with no arguments returns an object containing all headers. 21478 */ 21479function headersGetter(headers) { 21480 var headersObj; 21481 21482 return function(name) { 21483 if (!headersObj) headersObj = parseHeaders(headers); 21484 21485 if (name) { 21486 var value = headersObj[lowercase(name)]; 21487 if (value === undefined) { 21488 value = null; 21489 } 21490 return value; 21491 } 21492 21493 return headersObj; 21494 }; 21495} 21496 21497 21498/** 21499 * Chain all given functions 21500 * 21501 * This function is used for both request and response transforming 21502 * 21503 * @param {*} data Data to transform. 21504 * @param {function(string=)} headers HTTP headers getter fn. 21505 * @param {number} status HTTP status code of the response. 21506 * @param {(Function|Array.<Function>)} fns Function or an array of functions. 21507 * @returns {*} Transformed data. 21508 */ 21509function transformData(data, headers, status, fns) { 21510 if (isFunction(fns)) { 21511 return fns(data, headers, status); 21512 } 21513 21514 forEach(fns, function(fn) { 21515 data = fn(data, headers, status); 21516 }); 21517 21518 return data; 21519} 21520 21521 21522function isSuccess(status) { 21523 return 200 <= status && status < 300; 21524} 21525 21526 21527/** 21528 * @ngdoc provider 21529 * @name $httpProvider 21530 * @this 21531 * 21532 * @description 21533 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. 21534 * */ 21535function $HttpProvider() { 21536 /** 21537 * @ngdoc property 21538 * @name $httpProvider#defaults 21539 * @description 21540 * 21541 * Object containing default values for all {@link ng.$http $http} requests. 21542 * 21543 * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with 21544 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses 21545 * by default. See {@link $http#caching $http Caching} for more information. 21546 * 21547 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. 21548 * Defaults value is `'XSRF-TOKEN'`. 21549 * 21550 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the 21551 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. 21552 * 21553 * - **`defaults.headers`** - {Object} - Default headers for all $http requests. 21554 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on 21555 * setting default headers. 21556 * - **`defaults.headers.common`** 21557 * - **`defaults.headers.post`** 21558 * - **`defaults.headers.put`** 21559 * - **`defaults.headers.patch`** 21560 * 21561 * 21562 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function 21563 * used to the prepare string representation of request parameters (specified as an object). 21564 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}. 21565 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}. 21566 * 21567 * - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the 21568 * callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the 21569 * {@link $jsonpCallbacks} service. Defaults to `'callback'`. 21570 * 21571 **/ 21572 var defaults = this.defaults = { 21573 // transform incoming response data 21574 transformResponse: [defaultHttpResponseTransform], 21575 21576 // transform outgoing request data 21577 transformRequest: [function(d) { 21578 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d; 21579 }], 21580 21581 // default headers 21582 headers: { 21583 common: { 21584 'Accept': 'application/json, text/plain, */*' 21585 }, 21586 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), 21587 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), 21588 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) 21589 }, 21590 21591 xsrfCookieName: 'XSRF-TOKEN', 21592 xsrfHeaderName: 'X-XSRF-TOKEN', 21593 21594 paramSerializer: '$httpParamSerializer', 21595 21596 jsonpCallbackParam: 'callback' 21597 }; 21598 21599 var useApplyAsync = false;
21600 /** 21601 * @ngdoc method 21602 * @name $httpProvider#useApplyAsync 21603 * @description 21604 * 21605 * Configure $http service to combine processing of multiple http responses received at around 21606 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in 21607 * significant performance improvement for bigger applications that make many HTTP requests 21608 * concurrently (common during application bootstrap). 21609 * 21610 * Defaults to false. If no value is specified, returns the current configured value. 21611 * 21612 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred 21613 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window 21614 * to load and share the same digest cycle. 21615 * 21616 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. 21617 * otherwise, returns the current configured value. 21618 **/ 21619 this.useApplyAsync = function(value) { 21620 if (isDefined(value)) { 21621 useApplyAsync = !!value; 21622 return this; 21623 } 21624 return useApplyAsync; 21625 }; 21626 21627 /** 21628 * @ngdoc property 21629 * @name $httpProvider#interceptors 21630 * @description 21631 * 21632 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} 21633 * pre-processing of request or postprocessing of responses. 21634 * 21635 * These service factories are ordered by request, i.e. they are applied in the same order as the 21636 * array, on request, but reverse order, on response. 21637 * 21638 * {@link ng.$http#interceptors Interceptors detailed info} 21639 **/ 21640 var interceptorFactories = this.interceptors = []; 21641 21642 this.$get = ['$browser', '$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', '$sce', 21643 function($browser, $httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector, $sce) { 21644 21645 var defaultCache = $cacheFactory('$http'); 21646 21647 /** 21648 * Make sure that default param serializer is exposed as a function 21649 */ 21650 defaults.paramSerializer = isString(defaults.paramSerializer) ? 21651 $injector.get(defaults.paramSerializer) : defaults.paramSerializer; 21652 21653 /** 21654 * Interceptors stored in reverse order. Inner interceptors before outer interceptors. 21655 * The reversal is needed so that we can build up the interception chain around the 21656 * server request. 21657 */ 21658 var reversedInterceptors = []; 21659 21660 forEach(interceptorFactories, function(interceptorFactory) { 21661 reversedInterceptors.unshift(isString(interceptorFactory) 21662 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); 21663 }); 21664 21665 /** 21666 * @ngdoc service 21667 * @kind function 21668 * @name $http 21669 * @requires ng.$httpBackend 21670 * @requires $cacheFactory 21671 * @requires $rootScope 21672 * @requires $q 21673 * @requires $injector 21674 * 21675 * @description 21676 * The `$http` service is a core Angular service that facilitates communication with the remote 21677 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) 21678 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). 21679 * 21680 * For unit testing applications that use `$http` service, see 21681 * {@link ngMock.$httpBackend $httpBackend mock}. 21682 * 21683 * For a higher level of abstraction, please check out the {@link ngResource.$resource 21684 * $resource} service. 21685 * 21686 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by 21687 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage 21688 * it is important to familiarize yourself with these APIs and the guarantees they provide. 21689 * 21690 * 21691 * ## General usage 21692 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} — 21693 * that is used to generate an HTTP request and returns a {@link ng.$q promise}. 21694 * 21695 * ```js 21696 * // Simple GET request example: 21697 * $http({ 21698 * method: 'GET', 21699 * url: '/someUrl' 21700 * }).then(function successCallback(response) { 21701 * // this callback will be called asynchronously 21702 * // when the response is available 21703 * }, function errorCallback(response) { 21704 * // called asynchronously if an error occurs 21705 * // or server returns response with an error status. 21706 * }); 21707 * ``` 21708 * 21709 * The response object has these properties: 21710 * 21711 * - **data** – `{string|Object}` – The response body transformed with the transform 21712 * functions. 21713 * - **status** – `{number}` – HTTP status code of the response. 21714 * - **headers** – `{function([headerName])}` – Header getter function. 21715 * - **config** – `{Object}` – The configuration object that was used to generate the request. 21716 * - **statusText** – `{string}` – HTTP status text of the response. 21717 * 21718 * A response status code between 200 and 299 is considered a success status and will result in 21719 * the success callback being called. Any response status code outside of that range is 21720 * considered an error status and will result in the error callback being called. 21721 * Also, status codes less than -1 are normalized to zero. -1 usually means the request was 21722 * aborted, e.g. using a `config.timeout`. 21723 * Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning 21724 * that the outcome (success or error) will be determined by the final response status code. 21725 * 21726 * 21727 * ## Shortcut methods 21728 * 21729 * Shortcut methods are also available. All shortcut methods require passing in the URL, and 21730 * request data must be passed in for POST/PUT requests. An optional config can be passed as the 21731 * last argument. 21732 * 21733 * ```js 21734 * $http.get('/someUrl', config).then(successCallback, errorCallback); 21735 * $http.post('/someUrl', data, config).then(successCallback, errorCallback); 21736 * ``` 21737 * 21738 * Complete list of shortcut methods: 21739 * 21740 * - {@link ng.$http#get $http.get} 21741 * - {@link ng.$http#head $http.head} 21742 * - {@link ng.$http#post $http.post} 21743 * - {@link ng.$http#put $http.put} 21744 * - {@link ng.$http#delete $http.delete} 21745 * - {@link ng.$http#jsonp $http.jsonp} 21746 * - {@link ng.$http#patch $http.patch} 21747 * 21748 * 21749 * ## Writing Unit Tests that use $http 21750 * When unit testing (using {@link ngMock ngMock}), it is necessary to call 21751 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending 21752 * request using trained responses. 21753 * 21754 * ``` 21755 * $httpBackend.expectGET(...); 21756 * $http.get(...); 21757 * $httpBackend.flush(); 21758 * ``` 21759 * 21760 * ## Setting HTTP Headers 21761 * 21762 * The $http service will automatically add certain HTTP headers to all requests. These defaults 21763 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration 21764 * object, which currently contains this default configuration: 21765 * 21766 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): 21767 * - <code>Accept: application/json, text/plain, \*/\*</code> 21768 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) 21769 * - `Content-Type: application/json` 21770 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) 21771 * - `Content-Type: application/json` 21772 *
21773 * To add or overwrite these defaults, simply add or remove a property from these configuration 21774 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object 21775 * with the lowercased HTTP method name as the key, e.g. 21776 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`. 21777 * 21778 * The defaults can also be set at runtime via the `$http.defaults` object in the same 21779 * fashion. For example: 21780 * 21781 * ``` 21782 * module.run(function($http) { 21783 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'; 21784 * }); 21785 * ``` 21786 * 21787 * In addition, you can supply a `headers` property in the config object passed when 21788 * calling `$http(config)`, which overrides the defaults without changing them globally. 21789 * 21790 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, 21791 * Use the `headers` property, setting the desired header to `undefined`. For example: 21792 * 21793 * ```js 21794 * var req = { 21795 * method: 'POST', 21796 * url: 'http://example.com', 21797 * headers: { 21798 * 'Content-Type': undefined 21799 * }, 21800 * data: { test: 'test' } 21801 * } 21802 * 21803 * $http(req).then(function(){...}, function(){...}); 21804 * ``` 21805 * 21806 * ## Transforming Requests and Responses 21807 * 21808 * Both requests and responses can be transformed using transformation functions: `transformRequest` 21809 * and `transformResponse`. These properties can be a single function that returns 21810 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions, 21811 * which allows you to `push` or `unshift` a new transformation function into the transformation chain. 21812 * 21813 * <div class="alert alert-warning"> 21814 * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline. 21815 * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference). 21816 * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest 21817 * function will be reflected on the scope and in any templates where the object is data-bound. 21818 * To prevent this, transform functions should have no side-effects. 21819 * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return. 21820 * </div> 21821 * 21822 * ### Default Transformations 21823 * 21824 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and 21825 * `defaults.transformResponse` properties. If a request does not provide its own transformations 21826 * then these will be applied. 21827 * 21828 * You can augment or replace the default transformations by modifying these properties by adding to or 21829 * replacing the array. 21830 * 21831 * Angular provides the following default transformations: 21832 * 21833 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`): 21834 * 21835 * - If the `data` property of the request configuration object contains an object, serialize it 21836 * into JSON format. 21837 * 21838 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`): 21839 * 21840 * - If XSRF prefix is detected, strip it (see Security Considerations section below). 21841 * - If JSON response is detected,
vendor: 7,183 bytes, lines 21841-22001
21841 deserialize it using a JSON parser. 21842 * 21843 * 21844 * ### Overriding the Default Transformations Per Request 21845 * 21846 * If you wish to override the request/response transformations only for a single request then provide 21847 * `transformRequest` and/or `transformResponse` properties on the configuration object passed 21848 * into `$http`. 21849 * 21850 * Note that if you provide these properties on the config object the default transformations will be 21851 * overwritten. If you wish to augment the default transformations then you must include them in your 21852 * local transformation array. 21853 * 21854 * The following code demonstrates adding a new response transformation to be run after the default response 21855 * transformations have been run. 21856 * 21857 * ```js 21858 * function appendTransform(defaults, transform) { 21859 * 21860 * // We can't guarantee that the default transformation is an array 21861 * defaults = angular.isArray(defaults) ? defaults : [defaults]; 21862 * 21863 * // Append the new transformation to the defaults 21864 * return defaults.concat(transform); 21865 * } 21866 * 21867 * $http({ 21868 * url: '...', 21869 * method: 'GET', 21870 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) { 21871 * return doTransform(value); 21872 * }) 21873 * }); 21874 * ``` 21875 * 21876 * 21877 * ## Caching 21878 * 21879 * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must 21880 * set the config.cache value or the default cache value to TRUE or to a cache object (created 21881 * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes 21882 * precedence over the default cache value. 21883 * 21884 * In order to: 21885 * * cache all responses - set the default cache value to TRUE or to a cache object 21886 * * cache a specific response - set config.cache value to TRUE or to a cache object 21887 * 21888 * If caching is enabled, but neither the default cache nor config.cache are set to a cache object, 21889 * then the default `$cacheFactory("$http")` object is used. 21890 * 21891 * The default cache value can be set by updating the 21892 * {@link ng.$http#defaults `$http.defaults.cache`} property or the 21893 * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property. 21894 * 21895 * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using 21896 * the relevant cache object. The next time the same request is made, the response is returned 21897 * from the cache without sending a request to the server. 21898 * 21899 * Take note that: 21900 * 21901 * * Only GET and JSONP requests are cached. 21902 * * The cache key is the request URL including search parameters; headers are not considered. 21903 * * Cached responses are returned asynchronously, in the same way as responses from the server. 21904 * * If multiple identical requests are made using the same cache, which is not yet populated, 21905 * one request will be made to the server and remaining requests will return the same response. 21906 * * A cache-control header on the response does not affect if or how responses are cached. 21907 * 21908 * 21909 * ## Interceptors 21910 * 21911 * Before you start creating interceptors, be sure to understand the 21912 * {@link ng.$q $q and deferred/promise APIs}. 21913 * 21914 * For purposes of global error handling, authentication, or any kind of synchronous or 21915 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be 21916 * able to intercept requests before they are handed to the server and 21917 * responses before they are handed over to the application code that 21918 * initiated these requests. The interceptors leverage the {@link ng.$q 21919 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. 21920 * 21921 * The interceptors are service factories that are registered with the `$httpProvider` by 21922 * adding them to the `$httpProvider.interceptors` array. The factory is called and 21923 * injected with dependencies (if specified) and returns the interceptor. 21924 * 21925 * There are two kinds of interceptors (and two kinds of rejection interceptors): 21926 * 21927 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to 21928 * modify the `config` object or create a new one. The function needs to return the `config` 21929 * object directly, or a promise containing the `config` or a new `config` object. 21930 * * `requestError`: interceptor gets called when a previous interceptor threw an error or 21931 * resolved with a rejection. 21932 * * `response`: interceptors get called with http `response` object. The function is free to 21933 * modify the `response` object or create a new one. The function needs to return the `response` 21934 * object directly, or as a promise containing the `response` or a new `response` object. 21935 * * `responseError`: interceptor gets called when a previous interceptor threw an error or 21936 * resolved with a rejection. 21937 * 21938 * 21939 * ```js 21940 * // register the interceptor as a service 21941 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { 21942 * return { 21943 * // optional method 21944 * 'request': function(config) { 21945 * // do something on success 21946 * return config; 21947 * }, 21948 * 21949 * // optional method 21950 * 'requestError': function(rejection) { 21951 * // do something on error 21952 * if (canRecover(rejection)) { 21953 * return responseOrNewPromise 21954 * } 21955 * return $q.reject(rejection); 21956 * }, 21957 * 21958 * 21959 * 21960 * // optional method 21961 * 'response': function(response) { 21962 * // do something on success 21963 * return response; 21964 * }, 21965 * 21966 * // optional method 21967 * 'responseError': function(rejection) { 21968 * // do something on error 21969 * if (canRecover(rejection)) { 21970 * return responseOrNewPromise 21971 * } 21972 * return $q.reject(rejection); 21973 * } 21974 * }; 21975 * }); 21976 * 21977 * $httpProvider.interceptors.push('myHttpInterceptor'); 21978 * 21979 * 21980 * // alternatively, register the interceptor via an anonymous factory 21981 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { 21982 * return { 21983 * 'request': function(config) { 21984 * // same as above 21985 * }, 21986 * 21987 * 'response': function(response) { 21988 * // same as above 21989 * } 21990 * }; 21991 * }); 21992 * ``` 21993 * 21994 * ## Security Considerations 21995 * 21996 * When designing web applications, consider security threats from: 21997 * 21998 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) 21999 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) 22000 * 22001 * Both server and the client must cooperate in order to eliminate these threats. Angular
22001 comes 22002 * pre-configured with strategies that address these issues, but for this to work backend server 22003 * cooperation is required. 22004 * 22005 * ### JSON Vulnerability Protection 22006 * 22007 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) 22008 * allows third party website to turn your JSON resource URL into 22009 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To 22010 * counter this your server can prefix all JSON requests with following string `")]}',\n"`. 22011 * Angular will automatically strip the prefix before processing it as JSON. 22012 * 22013 * For example if your server needs to return: 22014 * ```js 22015 * ['one','two'] 22016 * ``` 22017 * 22018 * which is vulnerable to attack, your server can return: 22019 * ```js 22020 * )]}', 22021 * ['one','two'] 22022 * ``` 22023 * 22024 * Angular will strip the prefix, before processing the JSON. 22025 * 22026 * 22027 * ### Cross Site Request Forgery (XSRF) Protection 22028 * 22029 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by 22030 * which the attacker can trick an authenticated user into unknowingly executing actions on your 22031 * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the 22032 * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP 22033 * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the 22034 * cookie, your server can be assured that the XHR came from JavaScript running on your domain. 22035 * The header will not be set for cross-domain requests. 22036 * 22037 * To take advantage of this, your server needs to set a token in a JavaScript readable session 22038 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the 22039 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure 22040 * that only JavaScript running on your domain could have sent the request. The token must be 22041 * unique for each user and must be verifiable by the server (to prevent the JavaScript from 22042 * making up its own tokens). We recommend that the token is a digest of your site's 22043 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) 22044 * for added security. 22045 * 22046 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName 22047 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, 22048 * or the per-request config object. 22049 * 22050 * In order to prevent collisions in environments where multiple Angular apps share the 22051 * same domain or subdomain, we recommend that each application uses unique cookie name. 22052 * 22053 * @param {object} config Object describing the request to be made and how it should be 22054 * processed. The object has following properties: 22055 * 22056 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) 22057 * - **url** – `{string|TrustedObject}` – Absolute or relative URL of the resource that is being requested; 22058 * or an object created by a call to `$sce.trustAsResourceUrl(url)`. 22059 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized 22060 * with the `paramSerializer` and appended as GET parameters. 22061 * - **data** – `{string|Object}` – Data to be sent as the request message data. 22062 * - **headers** – `{Object}` – Map of strings or functions which return strings representing 22063 * HTTP headers to send to the server. If the return value of a function is null, the 22064 * header will not be sent. Functions accept a config object as an argument. 22065 * - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object. 22066 * To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`. 22067 * The handler will be called in the context of a `$apply` block. 22068 * - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload 22069 * object. To bind events to the XMLHttpRequest object, use `eventHandlers`. 22070 * The handler will be called in the context of a `$apply` block. 22071 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. 22072 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. 22073 * - **transformRequest** – 22074 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>
22074}` – 22075 * transform function or an array of such functions. The transform function takes the http 22076 * request body and headers and returns its transformed (typically serialized) version. 22077 * See {@link ng.$http#overriding-the-default-transformations-per-request 22078 * Overriding the Default Transformations} 22079 * - **transformResponse** – 22080 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` – 22081 * transform function or an array of such functions. The transform function takes the http 22082 * response body, headers and status and returns its transformed (typically deserialized) version. 22083 * See {@link ng.$http#overriding-the-default-transformations-per-request 22084 * Overriding the Default Transformations} 22085 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to 22086 * prepare the string representation of request parameters (specified as an object). 22087 * If specified as string, it is interpreted as function registered with the 22088 * {@link $injector $injector}, which means you can create your own serializer 22089 * by registering it as a {@link auto.$provide#service service}. 22090 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer}; 22091 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike} 22092 * - **cache** – `{boolean|Object}` – A boolean value or object created with 22093 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response. 22094 * See {@link $http#caching $http Caching} for more information. 22095 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} 22096 * that should abort the request when resolved. 22097 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the 22098 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) 22099 * for more information. 22100 * - **responseType** - `{string}` - see 22101 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype). 22102 * 22103 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object 22104 * when the request succeeds or fails. 22105 * 22106 * 22107 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending 22108 * requests. This is primarily meant to be used for debugging purposes. 22109 * 22110 * 22111 * @example 22112<example module="httpExample" name="http-service"> 22113<file name="index.html"> 22114 <div ng-controller="FetchController"> 22115 <select ng-model="method" aria-label="Request method"> 22116 <option>GET</option> 22117 <option>JSONP</option> 22118 </select> 22119 <input type="text" ng-model="url" size="80" aria-label="URL" /> 22120 <button id="fetchbtn" ng-click="fetch()">fetch</button><br> 22121 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button> 22122 <button id="samplejsonpbtn" 22123 ng-click="updateModel('JSONP', 22124 'https://angularjs.org/greet.php?name=Super%20Hero')"> 22125 Sample JSONP 22126 </button> 22127 <button id="invalidjsonpbtn" 22128 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist')"> 22129 Invalid JSONP 22130 </button> 22131 <pre>http status code: {{status}}</pre> 22132 <pre>http response data: {{data}}</pre> 22133 </div> 22134</file> 22135<file name="script.js"> 22136 angular.module('httpExample', []) 22137 .config(['$sceDelegateProvider', function($sceDelegateProvider) { 22138 // We must whitelist the JSONP endpoint that we are using to show that we trust it 22139 $sceDelegateProvider.resourceUrlWhitelist([ 22140 'self', 22141 'https://angularjs.org/**' 22142 ]); 22143 }]) 22144 .controller('FetchController', ['$scope', '$http', '$templateCache', 22145 function($scope, $http, $templateCache) { 22146 $scope.method = 'GET'; 22147 $scope.url = 'http-hello.html'; 22148 22149 $scope.fetch = function() { 22150 $scope.code = null; 22151 $scope.response = null; 22152 22153 $http({method: $scope.method, url: $scope.url, cache: $templateCache}). 22154 then(function(response) { 22155 $scope.status = response.status; 22156 $scope.data = response.data; 22157 }, function(response) { 22158 $scope.data = response.data || 'Request failed'; 22159 $scope.status = response.status; 22160 }); 22161 }; 22162 22163 $scope.updateModel = function(method, url) { 22164 $scope.method = method; 22165 $scope.url = url; 22166 }; 22167 }]); 22168</file> 22169<file name="http-hello.html"> 22170 Hello, $http! 22171</file> 22172<file name="protractor.js" type="protractor"> 22173 var status = element(by.binding('status')); 22174 var data = element(by.binding('data')); 22175 var fetchBtn = element(by.id('fetchbtn')); 22176 var sampleGetBtn = element(by.id('samplegetbtn')); 22177 var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); 22178 22179 it('should make an xhr GET request', function() { 22180 sampleGetBtn.click(); 22181 fetchBtn.click(); 22182 expect(status.getText()).toMatch('200'); 22183 expect(data.getText()).toMatch(/Hello, \$http!/); 22184 }); 22185 22186// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185 22187// it('should make a JSONP request to angularjs.org', function() { 22188// var sampleJsonpBtn = element(by.id('samplejsonpbtn')); 22189// sampleJsonpBtn.click(); 22190// fetchBtn.click(); 22191// expect(status.getText()).toMatch('200'); 22192// expect(data.getText()).toMatch(/Super Hero!/); 22193// }); 22194
vendor: 4,111 bytes, lines 22195-22313
22195 it('should make JSONP request to invalid URL and invoke the error handler', 22196 function() { 22197 invalidJsonpBtn.click(); 22198 fetchBtn.click(); 22199 expect(status.getText()).toMatch('0'); 22200 expect(data.getText()).toMatch('Request failed'); 22201 }); 22202</file> 22203</example> 22204 */ 22205 function $http(requestConfig) { 22206 22207 if (!isObject(requestConfig)) { 22208 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); 22209 } 22210 22211 if (!isString($sce.valueOf(requestConfig.url))) { 22212 throw minErr('$http')('badreq', 'Http request configuration url must be a string or a $sce trusted object. Received: {0}', requestConfig.url); 22213 } 22214 22215 var config = extend({ 22216 method: 'get', 22217 transformRequest: defaults.transformRequest, 22218 transformResponse: defaults.transformResponse, 22219 paramSerializer: defaults.paramSerializer, 22220 jsonpCallbackParam: defaults.jsonpCallbackParam 22221 }, requestConfig); 22222 22223 config.headers = mergeHeaders(requestConfig); 22224 config.method = uppercase(config.method); 22225 config.paramSerializer = isString(config.paramSerializer) ? 22226 $injector.get(config.paramSerializer) : config.paramSerializer; 22227 22228 $browser.$$incOutstandingRequestCount(); 22229 22230 var requestInterceptors = []; 22231 var responseInterceptors = []; 22232 var promise = $q.resolve(config); 22233 22234 // apply interceptors 22235 forEach(reversedInterceptors, function(interceptor) { 22236 if (interceptor.request || interceptor.requestError) { 22237 requestInterceptors.unshift(interceptor.request, interceptor.requestError); 22238 } 22239 if (interceptor.response || interceptor.responseError) { 22240 responseInterceptors.push(interceptor.response, interceptor.responseError); 22241 } 22242 }); 22243 22244 promise = chainInterceptors(promise, requestInterceptors); 22245 promise = promise.then(serverRequest); 22246 promise = chainInterceptors(promise, responseInterceptors); 22247 promise = promise.finally(completeOutstandingRequest); 22248 22249 return promise; 22250 22251 22252 function chainInterceptors(promise, interceptors) { 22253 for (var i = 0, ii = interceptors.length; i < ii;) { 22254 var thenFn = interceptors[i++]; 22255 var rejectFn = interceptors[i++]; 22256 22257 promise = promise.then(thenFn, rejectFn); 22258 } 22259 22260 interceptors.length = 0; 22261 22262 return promise; 22263 } 22264 22265 function completeOutstandingRequest() { 22266 $browser.$$completeOutstandingRequest(noop); 22267 } 22268 22269 function executeHeaderFns(headers, config) { 22270 var headerContent, processedHeaders = {}; 22271 22272 forEach(headers, function(headerFn, header) { 22273 if (isFunction(headerFn)) { 22274 headerContent = headerFn(config); 22275 if (headerContent != null) { 22276 processedHeaders[header] = headerContent; 22277 } 22278 } else { 22279 processedHeaders[header] = headerFn; 22280 } 22281 }); 22282 22283 return processedHeaders; 22284 } 22285 22286 function mergeHeaders(config) { 22287 var defHeaders = defaults.headers, 22288 reqHeaders = extend({}, config.headers), 22289 defHeaderName, lowercaseDefHeaderName, reqHeaderName; 22290 22291 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); 22292 22293 // using for-in instead of forEach to avoid unnecessary iteration after header has been found 22294 defaultHeadersIteration: 22295 for (defHeaderName in defHeaders) { 22296 lowercaseDefHeaderName = lowercase(defHeaderName); 22297 22298 for (reqHeaderName in reqHeaders) { 22299 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { 22300 continue defaultHeadersIteration; 22301 } 22302 } 22303 22304 reqHeaders[defHeaderName] = defHeaders[defHeaderName]; 22305 } 22306 22307 // execute if header value is a function for merged headers 22308 return executeHeaderFns(reqHeaders, shallowCopy(config)); 22309 } 22310 22311 function serverRequest(config) { 22312 var headers = config.headers; 22313 var reqData = transformData(config.data, headersGetter(headers), undefined, c
vendor: 20,279 bytes, lines 22313-22918
22313onfig.transformRequest); 22314 22315 // strip content-type if data is undefined 22316 if (isUndefined(reqData)) { 22317 forEach(headers, function(value, header) { 22318 if (lowercase(header) === 'content-type') { 22319 delete headers[header]; 22320 } 22321 }); 22322 } 22323 22324 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { 22325 config.withCredentials = defaults.withCredentials; 22326 } 22327 22328 // send request 22329 return sendReq(config, reqData).then(transformResponse, transformResponse); 22330 } 22331 22332 function transformResponse(response) { 22333 // make a copy since the response must be cacheable 22334 var resp = extend({}, response); 22335 resp.data = transformData(response.data, response.headers, response.status, 22336 config.transformResponse); 22337 return (isSuccess(response.status)) 22338 ? resp 22339 : $q.reject(resp); 22340 } 22341 } 22342 22343 $http.pendingRequests = []; 22344 22345 /** 22346 * @ngdoc method 22347 * @name $http#get 22348 * 22349 * @description 22350 * Shortcut method to perform `GET` request. 22351 * 22352 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; 22353 * or an object created by a call to `$sce.trustAsResourceUrl(url)`. 22354 * @param {Object=} config Optional configuration object 22355 * @returns {HttpPromise} Future object 22356 */ 22357 22358 /** 22359 * @ngdoc method 22360 * @name $http#delete 22361 * 22362 * @description 22363 * Shortcut method to perform `DELETE` request. 22364 * 22365 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; 22366 * or an object created by a call to `$sce.trustAsResourceUrl(url)`. 22367 * @param {Object=} config Optional configuration object 22368 * @returns {HttpPromise} Future object 22369 */ 22370 22371 /** 22372 * @ngdoc method 22373 * @name $http#head 22374 * 22375 * @description 22376 * Shortcut method to perform `HEAD` request. 22377 * 22378 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; 22379 * or an object created by a call to `$sce.trustAsResourceUrl(url)`. 22380 * @param {Object=} config Optional configuration object 22381 * @returns {HttpPromise} Future object 22382 */ 22383 22384 /** 22385 * @ngdoc method 22386 * @name $http#jsonp 22387 * 22388 * @description 22389 * Shortcut method to perform `JSONP` request. 22390 * 22391 * Note that, since JSONP requests are sensitive because the response is given full access to the browser, 22392 * the url must be declared, via {@link $sce} as a trusted resource URL. 22393 * You can trust a URL by adding it to the whitelist via 22394 * {@link $sceDelegateProvider#resourceUrlWhitelist `$sceDelegateProvider.resourceUrlWhitelist`} or 22395 * by explicitly trusting the URL via {@link $sce#trustAsResourceUrl `$sce.trustAsResourceUrl(url)`}. 22396 * 22397 * JSONP requests must specify a callback to be used in the response from the server. This callback 22398 * is passed as a query parameter in the request. You must specify the name of this parameter by 22399 * setting the `jsonpCallbackParam` property on the request config object. 22400 * 22401 * ``` 22402 * $http.jsonp('some/trusted/url', {jsonpCallbackParam: 'callback'}) 22403 * ``` 22404 * 22405 * You can also specify a default callback parameter name in `$http.defaults.jsonpCallbackParam`. 22406 * Initially this is set to `'callback'`. 22407 * 22408 * <div class="alert alert-danger"> 22409 * You can no longer use the `JSON_CALLBACK` string as a placeholder for specifying where the callback 22410 * parameter value should go. 22411 * </div> 22412 * 22413 * If you would like to customise where and how the callbacks are stored then try overriding 22414 * or decorating the {@link $jsonpCallbacks} service. 22415 * 22416 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; 22417 * or an object created by a call to `$sce.trustAsResourceUrl(url)`. 22418 * @param {Object=} config Optional configuration object 22419 * @returns {HttpPromise} Future object 22420 */ 22421 createShortMethods('get', 'delete', 'head', 'jsonp'); 22422 22423 /** 22424 * @ngdoc method 22425 * @name $http#post 22426 * 22427 * @description 22428 * Shortcut method to perform `POST` request. 22429 * 22430 * @param {string} url Relative or absolute URL specifying the destination of the request 22431 * @param {*} data Request content 22432 * @param {Object=} config Optional configuration object 22433 * @returns {HttpPromise} Future object 22434 */ 22435 22436 /** 22437 * @ngdoc method 22438 * @name $http#put 22439 * 22440 * @description 22441 * Shortcut method to perform `PUT` request. 22442 * 22443 * @param {string} url Relative or absolute URL specifying the destination of the request 22444 * @param {*} data Request content 22445 * @param {Object=} config Optional configuration object 22446 * @returns {HttpPromise} Future object 22447 */ 22448 22449 /** 22450 * @ngdoc method 22451 * @name $http#patch 22452 * 22453 * @description 22454 * Shortcut method to perform `PATCH` request. 22455 * 22456 * @param {string} url Relative or absolute URL specifying the destination of the request 22457 * @param {*} data Request content 22458 * @param {Object=} config Optional configuration object 22459 * @returns {HttpPromise} Future object 22460 */ 22461 createShortMethodsWithData('post', 'put', 'patch'); 22462 22463 /** 22464 * @ngdoc property 22465 * @name $http#defaults 22466 * 22467 * @description 22468 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of 22469 * default headers, withCredentials as well as request and response transformations. 22470 * 22471 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. 22472 */ 22473 $http.defaults = defaults; 22474 22475 22476 return $http; 22477 22478 22479 function createShortMethods(names) { 22480 forEach(arguments, function(name) { 22481 $http[name] = function(url, config) { 22482 return $http(extend({}, config || {}, { 22483 method: name, 22484 url: url 22485 })); 22486 }; 22487 }); 22488 } 22489 22490 22491 function createShortMethodsWithData(name) { 22492 forEach(arguments, function(name) { 22493 $http[name] = function(url, data, config) { 22494 return $http(extend({}, config || {}, { 22495 method: name, 22496 url: url, 22497 data: data 22498 })); 22499 }; 22500 }); 22501 } 22502 22503 22504 /** 22505 * Makes the request. 22506 * 22507 * !!! ACCESSES CLOSURE VARS: 22508 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests 22509 */ 22510 function sendReq(config, reqData) { 22511 var deferred = $q.defer(), 22512 promise = deferred.promise, 22513 cache, 22514 cachedResp, 22515 reqHeaders = config.headers, 22516 isJsonp = lowercase(config.method) === 'jsonp', 22517 url = config.url; 22518 22519 if (isJsonp) { 22520 // JSONP is a pretty sensitive operation where we're allowing a script to have full access to 22521 // our DOM and JS space. So we require that the URL satisfies SCE.RESOURCE_URL. 22522 url = $sce.getTrustedResourceUrl(url); 22523 } else if (!isString(url)) { 22524 // If it is not a string then the URL must be a $sce trusted object 22525 url = $sce.valueOf(url); 22526 } 22527 22528 url = buildUrl(url, config.paramSerializer(config.params)); 22529 22530 if (isJsonp) { 22531 // Check the url and add the JSONP callback placeholder 22532 url = sanitizeJsonpCallbackParam(url, config.jsonpCallbackParam); 22533 } 22534 22535 $http.pendingRequests.push(config); 22536 promise.then(removePendingReq, removePendingReq); 22537 22538 if ((config.cache || defaults.cache) && config.cache !== false && 22539 (config.method === 'GET' || config.method === 'JSONP')) { 22540 cache = isObject(config.cache) ? config.cache 22541 : isObject(/** @type {?} */ (defaults).cache) 22542 ? /** @type {?} */ (defaults).cache 22543 : defaultCache; 22544 } 22545 22546 if (cache) { 22547 cachedResp = cache.get(url); 22548 if (isDefined(cachedResp)) { 22549 if (isPromiseLike(cachedResp)) { 22550 // cached request has already been sent, but there is no response yet 22551 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult); 22552 } else { 22553 // serving from cache 22554 if (isArray(cachedResp)) { 22555 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); 22556 } else { 22557 resolvePromise(cachedResp, 200, {}, 'OK'); 22558 } 22559 } 22560 } else { 22561 // put the promise for the non-transformed response into cache as a placeholder 22562 cache.put(url, promise); 22563 } 22564 } 22565 22566 22567 // if we won't have the response in cache, set the xsrf headers and 22568 // send the request to the backend 22569 if (isUndefined(cachedResp)) { 22570 var xsrfValue = urlIsSameOrigin(config.url) 22571 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName] 22572 : undefined; 22573 if (xsrfValue) { 22574 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; 22575 } 22576 22577 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, 22578 config.withCredentials, config.responseType, 22579 createApplyHandlers(config.eventHandlers), 22580 createApplyHandlers(config.uploadEventHandlers)); 22581 } 22582 22583 return promise; 22584 22585 function createApplyHandlers(eventHandlers) { 22586 if (eventHandlers) { 22587 var applyHandlers = {}; 22588 forEach(eventHandlers, function(eventHandler, key) { 22589 applyHandlers[key] = function(event) { 22590 if (useApplyAsync) { 22591 $rootScope.$applyAsync(callEventHandler); 22592 } else if ($rootScope.$$phase) { 22593 callEventHandler(); 22594 } else { 22595 $rootScope.$apply(callEventHandler); 22596 } 22597 22598 function callEventHandler() { 22599 eventHandler(event); 22600 } 22601 }; 22602 }); 22603 return applyHandlers; 22604 } 22605 } 22606 22607 22608 /** 22609 * Callback registered to $httpBackend(): 22610 * - caches the response if desired 22611 * - resolves the raw $http promise 22612 * - calls $apply 22613 */ 22614 function done(status, response, headersString, statusText) { 22615 if (cache) { 22616 if (isSuccess(status)) { 22617 cache.put(url, [status, response, parseHeaders(headersString), statusText]); 22618 } else { 22619 // remove promise from the cache 22620 cache.remove(url); 22621 } 22622 } 22623 22624 function resolveHttpPromise() { 22625 resolvePromise(response, status, headersString, statusText); 22626 } 22627 22628 if (useApplyAsync) { 22629 $rootScope.$applyAsync(resolveHttpPromise); 22630 } else { 22631 resolveHttpPromise(); 22632 if (!$rootScope.$$phase) $rootScope.$apply(); 22633 } 22634 } 22635 22636 22637 /** 22638 * Resolves the raw $http promise. 22639 */ 22640 function resolvePromise(response, status, headers, statusText) { 22641 //status: HTTP response status code, 0, -1 (aborted by timeout / promise) 22642 status = status >= -1 ? status : 0; 22643 22644 (isSuccess(status) ? deferred.resolve : deferred.reject)({ 22645 data: response, 22646 status: status, 22647 headers: headersGetter(headers), 22648 config: config, 22649 statusText: statusText 22650 }); 22651 } 22652 22653 function resolvePromiseWithResult(result) { 22654 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText); 22655 } 22656 22657 function removePendingReq() { 22658 var idx = $http.pendingRequests.indexOf(config); 22659 if (idx !== -1) $http.pendingRequests.splice(idx, 1); 22660 } 22661 } 22662 22663 22664 function buildUrl(url, serializedParams) { 22665 if (serializedParams.length > 0) { 22666 url += ((url.indexOf('?') === -1) ? '?' : '&') + serializedParams; 22667 } 22668 return url; 22669 } 22670 22671 function sanitizeJsonpCallbackParam(url, key) { 22672 if (/[&?][^=]+=JSON_CALLBACK/.test(url)) { 22673 // Throw if the url already contains a reference to JSON_CALLBACK 22674 throw $httpMinErr('badjsonp', 'Illegal use of JSON_CALLBACK in url, "{0}"', url); 22675 } 22676 22677 var callbackParamRegex = new RegExp('[&?]' + key + '='); 22678 if (callbackParamRegex.test(url)) { 22679 // Throw if the callback param was already provided 22680 throw $httpMinErr('badjsonp', 'Illegal use of callback param, "{0}", in url, "{1}"', key, url); 22681 } 22682 22683 // Add in the JSON_CALLBACK callback param value 22684 url += ((url.indexOf('?') === -1) ? '?' : '&') + key + '=JSON_CALLBACK'; 22685 22686 return url; 22687 } 22688 }]; 22689} 22690 22691/** 22692 * @ngdoc service 22693 * @name $xhrFactory 22694 * @this 22695 * 22696 * @description 22697 * Factory function used to create XMLHttpRequest objects. 22698 * 22699 * Replace or decorate this service to create your own custom XMLHttpRequest objects. 22700 * 22701 * ``` 22702 * angular.module('myApp', []) 22703 * .factory('$xhrFactory', function() { 22704 * return function createXhr(method, url) { 22705 * return new window.XMLHttpRequest({mozSystem: true}); 22706 * }; 22707 * }); 22708 * ``` 22709 * 22710 * @param {string} method HTTP method of the request (GET, POST, PUT, ..) 22711 * @param {string} url URL of the request. 22712 */ 22713function $xhrFactoryProvider() { 22714 this.$get = function() { 22715 return function createXhr() { 22716 return new window.XMLHttpRequest(); 22717 }; 22718 }; 22719} 22720 22721/** 22722 * @ngdoc service 22723 * @name $httpBackend 22724 * @requires $jsonpCallbacks 22725 * @requires $document 22726 * @requires $xhrFactory 22727 * @this 22728 * 22729 * @description 22730 * HTTP backend used by the {@link ng.$http service} that delegates to 22731 * XMLHttpRequest object or JSONP and deals with browser incompatibilities. 22732 * 22733 * You should never need to use this service directly, instead use the higher-level abstractions: 22734 * {@link ng.$http $http} or {@link ngResource.$resource $resource}. 22735 * 22736 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock 22737 * $httpBackend} which can be trained with responses. 22738 */ 22739function $HttpBackendProvider() { 22740 this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) { 22741 return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]); 22742 }]; 22743} 22744 22745function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { 22746 // TODO(vojta): fix the signature 22747 return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) { 22748 url = url || $browser.url(); 22749 22750 if (lowercase(method) === 'jsonp') { 22751 var callbackPath = callbacks.createCallback(url); 22752 var jsonpDone = jsonpReq(url, callbackPath, function(status, text) { 22753 // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING) 22754 var response = (status === 200) && callbacks.getResponse(callbackPath); 22755 completeRequest(callback, status, response, '', text); 22756 callbacks.removeCallback(callbackPath); 22757 }); 22758 } else { 22759 22760 var xhr = createXhr(method, url); 22761 22762 xhr.open(method, url, true); 22763 forEach(headers, function(value, key) { 22764 if (isDefined(value)) { 22765 xhr.setRequestHeader(key, value); 22766 } 22767 }); 22768 22769 xhr.onload = function requestLoaded() { 22770 var statusText = xhr.statusText || ''; 22771 22772 // responseText is the old-school way of retrieving response (supported by IE9) 22773 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) 22774 var response = ('response' in xhr) ? xhr.response : xhr.responseText; 22775 22776 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) 22777 var status = xhr.status === 1223 ? 204 : xhr.status; 22778 22779 // fix status code when it is 0 (0 status is undocumented). 22780 // Occurs when accessing file resources or on Android 4.1 stock browser 22781 // while retrieving files from application cache. 22782 if (status === 0) { 22783 status = response ? 200 : urlResolve(url).protocol === 'file' ? 404 : 0; 22784 } 22785 22786 completeRequest(callback, 22787 status, 22788 response, 22789 xhr.getAllResponseHeaders(), 22790 statusText); 22791 }; 22792 22793 var requestError = function() { 22794 // The response is always empty 22795 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error 22796 completeRequest(callback, -1, null, null, ''); 22797 }; 22798 22799 xhr.onerror = requestError; 22800 xhr.onabort = requestError; 22801 xhr.ontimeout = requestError; 22802 22803 forEach(eventHandlers, function(value, key) { 22804 xhr.addEventListener(key, value); 22805 }); 22806 22807 forEach(uploadEventHandlers, function(value, key) { 22808 xhr.upload.addEventListener(key, value); 22809 }); 22810 22811 if (withCredentials) { 22812 xhr.withCredentials = true; 22813 } 22814 22815 if (responseType) { 22816 try { 22817 xhr.responseType = responseType; 22818 } catch (e) { 22819 // WebKit added support for the json responseType value on 09/03/2013 22820 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are 22821 // known to throw when setting the value "json" as the response type. Other older 22822 // browsers implementing the responseType 22823 // 22824 // The json response type can be ignored if not supported, because JSON payloads are 22825 // parsed on the client-side regardless. 22826 if (responseType !== 'json') { 22827 throw e; 22828 } 22829 } 22830 } 22831 22832 xhr.send(isUndefined(post) ? null : post); 22833 } 22834 22835 if (timeout > 0) { 22836 var timeoutId = $browserDefer(timeoutRequest, timeout); 22837 } else if (isPromiseLike(timeout)) { 22838 timeout.then(timeoutRequest); 22839 } 22840 22841 22842 function timeoutRequest() { 22843 if (jsonpDone) { 22844 jsonpDone(); 22845 } 22846 if (xhr) { 22847 xhr.abort(); 22848 } 22849 } 22850 22851 function completeRequest(callback, status, response, headersString, statusText) { 22852 // cancel timeout and subsequent timeout promise resolution 22853 if (isDefined(timeoutId)) { 22854 $browserDefer.cancel(timeoutId); 22855 } 22856 jsonpDone = xhr = null; 22857 22858 callback(status, response, headersString, statusText); 22859 } 22860 }; 22861 22862 function jsonpReq(url, callbackPath, done) { 22863 url = url.replace('JSON_CALLBACK', callbackPath); 22864 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.: 22865 // - fetches local scripts via XHR and evals them 22866 // - adds and immediately removes script elements from the document 22867 var script = rawDocument.createElement('script'), callback = null; 22868 script.type = 'text/javascript'; 22869 script.src = url; 22870 script.async = true; 22871 22872 callback = function(event) { 22873 script.removeEventListener('load', callback); 22874 script.removeEventListener('error', callback); 22875 rawDocument.body.removeChild(script); 22876 script = null; 22877 var status = -1; 22878 var text = 'unknown'; 22879 22880 if (event) { 22881 if (event.type === 'load' && !callbacks.wasCalled(callbackPath)) { 22882 event = { type: 'error' }; 22883 } 22884 text = event.type; 22885 status = event.type === 'error' ? 404 : 200; 22886 } 22887 22888 if (done) { 22889 done(status, text); 22890 } 22891 }; 22892 22893 script.addEventListener('load', callback); 22894 script.addEventListener('error', callback); 22895 rawDocument.body.appendChild(script); 22896 return callback; 22897 } 22898} 22899 22900var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate'); 22901$interpolateMinErr.throwNoconcat = function(text) { 22902 throw $interpolateMinErr('noconcat', 22903 'Error while interpolating: {0}\nStrict Contextual Escaping disallows ' + 22904 'interpolations that concatenate multiple expressions when a trusted value is ' + 22905 'required. See http://docs.angularjs.org/api/ng.$sce', text); 22906}; 22907 22908$interpolateMinErr.interr = function(text, err) { 22909 return $interpolateMinErr('interr', 'Can\'t interpolate: {0}\n{1}', text, err.toString()); 22910}; 22911 22912/** 22913 * @ngdoc provider 22914 * @name $interpolateProvider 22915 * @this 22916 * 22917 * @description 22918 *
22919 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. 22920 * 22921 * <div class="alert alert-danger"> 22922 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular 22923 * template within a Python Jinja template (or any other template language). Mixing templating 22924 * languages is **very dangerous**. The embedding template language will not safely escape Angular 22925 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS) 22926 * security bugs! 22927 * </div> 22928 * 22929 * @example 22930<example name="custom-interpolation-markup" module="customInterpolationApp"> 22931<file name="index.html"> 22932<script> 22933 var customInterpolationApp = angular.module('customInterpolationApp', []); 22934 22935 customInterpolationApp.config(function($interpolateProvider) { 22936 $interpolateProvider.startSymbol('//'); 22937 $interpolateProvider.endSymbol('//'); 22938 }); 22939 22940 22941 customInterpolationApp.controller('DemoController', function() { 22942 this.label = "This binding is brought you by // interpolation symbols."; 22943 }); 22944</script> 22945<div ng-controller="DemoController as demo"> 22946 //demo.label// 22947</div> 22948</file> 22949<file name="protractor.js" type="protractor"> 22950 it('should interpolate binding with custom symbols', function() { 22951 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); 22952 }); 22953</file> 22954</example> 22955 */ 22956function $InterpolateProvider() { 22957 var startSymbol = '{{'; 22958 var endSymbol = '}}'; 22959 22960 /** 22961 * @ngdoc method 22962 * @name $interpolateProvider#startSymbol 22963 * @description 22964 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. 22965 * 22966 * @param {string=} value new value to set the starting symbol to. 22967 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. 22968 */ 22969 this.startSymbol = function(value) { 22970 if (value) { 22971 startSymbol = value; 22972 return this; 22973 } else { 22974 return startSymbol; 22975 } 22976 }; 22977 22978 /** 22979 * @ngdoc method 22980 * @name $interpolateProvider#endSymbol 22981 * @description 22982 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. 22983 * 22984 * @param {string=} value new value to set the ending symbol to. 22985 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. 22986 */ 22987 this.endSymbol = function(value) { 22988 if (value) { 22989 endSymbol = value; 22990 return this; 22991 } else { 22992 return endSymbol; 22993 } 22994 }; 22995 22996 22997 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { 22998 var startSymbolLength = startSymbol.length, 22999 endSymbolLength = endSymbol.length, 23000 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'), 23001 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g'); 23002 23003 function escape(ch) { 23004 return '\\\\\\' + ch; 23005 } 23006 23007 function unescapeText(text) { 23008 return text.replace(escapedStartRegexp, startSymbol). 23009 replace(escapedEndRegexp, endSymbol); 23010 } 23011 23012 // TODO: this is the same as the constantWatchDelegate in parse.js 23013 function constantWatchDelegate(scope, listener, objectEquality, constantInterp) { 23014 var unwatch = scope.$watch(function constantInterpolateWatch(scope) { 23015 unwatch(); 23016 return constantInterp(scope); 23017 }, listener, objectEquality); 23018 return unwatch; 23019 } 23020 23021 /** 23022 * @ngdoc service 23023 * @name $interpolate 23024 * @kind function 23025 * 23026 * @requires $parse 23027 * @requires $sce 23028 * 23029 * @description 23030 * 23031 * Compiles a string with markup into an interpolation function. This service is used by the 23032 * HTML {@link ng.$compile $compile} service for data binding. See 23033 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the 23034 * interpolation markup. 23035 * 23036 * 23037 * ```js 23038 * var $interpolate = ...; // injected 23039 * var exp = $interpolate('Hello {{name | uppercase}}!'); 23040 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!'); 23041 * ``` 23042 * 23043 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is 23044 * `true`, the interpolation function will return `undefined` unless all embedded expressions 23045 * evaluate to a value other than `undefined`. 23046 * 23047 * ```js 23048 * var $interpolate = ...; // injected 23049 * var context = {greeting: 'Hello', name: undefined }; 23050 * 23051 * // default "forgiving" mode 23052 * var exp = $interpolate('{{greeting}} {{name}}!'); 23053 * expect(exp(context)).toEqual('Hello !'); 23054 * 23055 * // "allOrNothing" mode 23056 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true); 23057 * expect(exp(context)).toBeUndefined(); 23058 * context.name = 'Angular'; 23059 * expect(exp(context)).toEqual('Hello Angular!'); 23060 * ``` 23061 * 23062 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior. 23063 * 23064 * #### Escaped Interpolation 23065 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers 23066 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash). 23067 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression 23068 * or binding. 23069 * 23070 * This enables web-servers to prevent script injection attacks and defacing attacks, to some 23071 * degree, while also enabling code examples to work without relying on the 23072 * {@link ng.directive:ngNonBindable ngNonBindable} directive. 23073 * 23074 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data, 23075 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all 23076 * interpolation start/end markers with their escaped counterparts.** 23077 * 23078 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered 23079 * output when the $interpolate service processes the text. So, for HTML elements interpolated 23080 * by {@link ng.$compile $compile}
23080, or otherwise interpolated with the `mustHaveExpression` parameter 23081 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such, 23082 * this is typically useful only when user-data is used in rendering a template from the server, or 23083 * when otherwise untrusted data is used by a directive. 23084 * 23085 * <example name="interpolation"> 23086 * <file name="index.html"> 23087 * <div ng-init="username='A user'"> 23088 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\} 23089 * </p> 23090 * <p><strong>{{username}}</strong> attempts to inject code which will deface the 23091 * application, but fails to accomplish their task, because the server has correctly 23092 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash) 23093 * characters.</p> 23094 * <p>Instead, the result of the attempted script injection is visible, and can be removed 23095 * from the database by an administrator.</p> 23096 * </div> 23097 * </file> 23098 * </example> 23099 * 23100 * @knownIssue 23101 * It is currently not possible for an interpolated expression to contain the interpolation end 23102 * symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e. 23103 * an interpolated expression consisting of a single-quote (`'`) and the `' }}` string. 23104 * 23105 * @knownIssue 23106 * All directives and components must use the standard `{{` `}}` interpolation symbols 23107 * in their templates. If you change the application interpolation symbols the {@link $compile} 23108 * service will attempt to denormalize the standard symbols to the custom symbols. 23109 * The denormalization process is not clever enough to know not to replace instances of the standard 23110 * symbols where they would not normally be treated as interpolation symbols. For example in the following 23111 * code snippet the closing braces of the literal object will get incorrectly denormalized: 23112 * 23113 * ``` 23114 * <div data-context='{"context":{"id":3,"type":"page"}}"> 23115 * ``` 23116 * 23117 * The workaround is to ensure that such instances are separated by whitespace: 23118 * ``` 23119 * <div data-context='{"context":{"id":3,"type":"page"} }"> 23120 * ``` 23121 * 23122 * See https://github.com/angular/angular.js/pull/14610#issuecomment-219401099 for more information. 23123 * 23124 * @param {string} text The text with markup to interpolate. 23125 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have 23126 * embedded expression in order to return an interpolation function. Strings with no 23127 * embedded expression will return null for the interpolation function. 23128 * @param {string=} trustedContext when provided, the returned function passes the interpolated 23129 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, 23130 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that 23131 * provides Strict Contextual Escaping for details. 23132 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined 23133 * unless all embedded expressions evaluate to a value other than `undefined`. 23134 * @returns {function(context)} an interpolation function which is used to compute the 23135 * interpolated string. The function has these parameters: 23136 * 23137 * - `context`: evaluation context for all expressions embedded in the interpolated text 23138 */ 23139 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) { 23140 // Provide a quick exit and simplified result function for text with no interpolation 23141 if (!text.length || text.indexOf(startSymbol) === -1) { 23142 var constantInterp; 23143 if (!mustHaveExpression) { 23144 var unescapedText = unescapeText(text); 23145 constantInterp = valueFn(unescapedText); 23146 constantInterp.exp = text; 23147 constantInterp.expressions = []; 23148 constantInterp.$$watchDelegate = constantWatchDelegate; 23149 } 23150 return constantInterp; 23151 } 23152 23153 allOrNothing = !!allOrNothing; 23154 var startIndex, 23155 endIndex, 23156 index = 0, 23157 expressions = [], 23158 parseFns = [], 23159 textLength = text.length, 23160 exp, 23161 concat = [], 23162 expressionPositions = []; 23163 23164 while (index < textLength) { 23165 if (((startIndex = text.indexOf(startSymbol, index)) !== -1) && 23166 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1)) { 23167 if (index !== startIndex) { 23168 concat.push(unescapeText(text.substring(index, startIndex))); 23169 } 23170 exp = text.substring(startIndex + startSymbolLength, endIndex); 23171 expressions.push(exp); 23172 parseFns.push($parse(exp, parseStringifyInterceptor));
vendor: 4,127 bytes, lines 23173-23298
23173 index = endIndex + endSymbolLength; 23174 expressionPositions.push(concat.length); 23175 concat.push(''); 23176 } else { 23177 // we did not find an interpolation, so we have to add the remainder to the separators array 23178 if (index !== textLength) { 23179 concat.push(unescapeText(text.substring(index))); 23180 } 23181 break; 23182 } 23183 } 23184 23185 // Concatenating expressions makes it hard to reason about whether some combination of 23186 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a 23187 // single expression be used for iframe[src], object[src], etc., we ensure that the value 23188 // that's used is assigned or constructed by some JS code somewhere that is more testable or 23189 // make it obvious that you bound the value to some user controlled value. This helps reduce 23190 // the load when auditing for XSS issues. 23191 if (trustedContext && concat.length > 1) { 23192 $interpolateMinErr.throwNoconcat(text); 23193 } 23194 23195 if (!mustHaveExpression || expressions.length) { 23196 var compute = function(values) { 23197 for (var i = 0, ii = expressions.length; i < ii; i++) { 23198 if (allOrNothing && isUndefined(values[i])) return; 23199 concat[expressionPositions[i]] = values[i]; 23200 } 23201 return concat.join(''); 23202 }; 23203 23204 var getValue = function(value) { 23205 return trustedContext ? 23206 $sce.getTrusted(trustedContext, value) : 23207 $sce.valueOf(value); 23208 }; 23209 23210 return extend(function interpolationFn(context) { 23211 var i = 0; 23212 var ii = expressions.length; 23213 var values = new Array(ii); 23214 23215 try { 23216 for (; i < ii; i++) { 23217 values[i] = parseFns[i](context); 23218 } 23219 23220 return compute(values); 23221 } catch (err) { 23222 $exceptionHandler($interpolateMinErr.interr(text, err)); 23223 } 23224 23225 }, { 23226 // all of these properties are undocumented for now 23227 exp: text, //just for compatibility with regular watchers created via $watch 23228 expressions: expressions, 23229 $$watchDelegate: function(scope, listener) { 23230 var lastValue; 23231 return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) { 23232 var currValue = compute(values); 23233 if (isFunction(listener)) { 23234 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); 23235 } 23236 lastValue = currValue; 23237 }); 23238 } 23239 }); 23240 } 23241 23242 function parseStringifyInterceptor(value) { 23243 try { 23244 value = getValue(value); 23245 return allOrNothing && !isDefined(value) ? value : stringify(value); 23246 } catch (err) { 23247 $exceptionHandler($interpolateMinErr.interr(text, err)); 23248 } 23249 } 23250 } 23251 23252 23253 /** 23254 * @ngdoc method 23255 * @name $interpolate#startSymbol 23256 * @description 23257 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. 23258 * 23259 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change 23260 * the symbol. 23261 * 23262 * @returns {string} start symbol. 23263 */ 23264 $interpolate.startSymbol = function() { 23265 return startSymbol; 23266 }; 23267 23268 23269 /** 23270 * @ngdoc method 23271 * @name $interpolate#endSymbol 23272 * @description 23273 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. 23274 * 23275 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change 23276 * the symbol. 23277 * 23278 * @returns {string} end symbol. 23279 */ 23280 $interpolate.endSymbol = function() { 23281 return endSymbol; 23282 }; 23283 23284 return $interpolate; 23285 }]; 23286} 23287 23288/** @this */ 23289function $IntervalProvider() { 23290 this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser', 23291 function($rootScope, $window, $q, $$q, $browser) { 23292 var intervals = {}; 23293 23294 23295 /** 23296 * @ngdoc service 23297 * @name $interval 23298 *
23299 * @description 23300 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay` 23301 * milliseconds. 23302 * 23303 * The return value of registering an interval function is a promise. This promise will be 23304 * notified upon each tick of the interval, and will be resolved after `count` iterations, or 23305 * run indefinitely if `count` is not defined. The value of the notification will be the 23306 * number of iterations that have run. 23307 * To cancel an interval, call `$interval.cancel(promise)`. 23308 * 23309 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to 23310 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that 23311 * time. 23312 * 23313 * <div class="alert alert-warning"> 23314 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished 23315 * with them. In particular they are not automatically destroyed when a controller's scope or a 23316 * directive's element are destroyed. 23317 * You should take this into consideration and make sure to always cancel the interval at the 23318 * appropriate moment. See the example below for more details on how and when to do this. 23319 * </div> 23320 * 23321 * @param {function()} fn A function that should be called repeatedly. If no additional arguments 23322 * are passed (see below), the function is called with the current iteration count. 23323 * @param {number} delay Number of milliseconds between each function call. 23324 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat 23325 * indefinitely. 23326 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise 23327 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. 23328 * @param {...*=} Pass additional parameters to the executed function. 23329 * @returns {promise} A promise which will be notified on each iteration. 23330 * 23331 * @example 23332 * <example module="intervalExample" name="interval-service"> 23333 * <file name="index.html"> 23334 * <script> 23335 * angular.module('intervalExample', []) 23336 * .controller('ExampleController', ['$scope', '$interval', 23337 * function($scope, $interval) { 23338 * $scope.format = 'M/d/yy h:mm:ss a'; 23339 * $scope.blood_1 = 100; 23340 * $scope.blood_2 = 120; 23341 * 23342 * var stop; 23343 * $scope.fight = function() { 23344 * // Don't start a new fight if we are already fighting 23345 * if ( angular.isDefined(stop) ) return; 23346 * 23347 * stop = $interval(function() { 23348 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) { 23349 * $scope.blood_1 = $scope.blood_1 - 3; 23350 * $scope.blood_2 = $scope.blood_2 - 4; 23351 * } else { 23352 * $scope.stopFight(); 23353 * } 23354 * }, 100); 23355 * }; 23356 * 23357 * $scope.stopFight = function() { 23358 * if (angular.isDefined(stop)) { 23359 * $interval.cancel(stop); 23360 * stop = undefined; 23361 * } 23362 * }; 23363 * 23364 * $scope.resetFight = function() { 23365 * $scope.blood_1 = 100; 23366 * $scope.blood_2 = 120; 23367 * }; 23368 * 23369 * $scope.$on('$destroy', function() { 23370 * // Make sure that the interval is destroyed too 23371 * $scope.stopFight(); 23372 * }); 23373 * }]) 23374 * // Register the 'myCurrentTime' directive factory method. 23375 * // We inject $interval and dateFilter service since the factory method is DI. 23376 * .directive('myCurrentTime', ['$interval', 'dateFilter', 23377 * function($interval, dateFilter) { 23378 * // return the directive link function. (compile function not needed) 23379 * return function(scope, element, attrs) { 23380 * var format, // date format 23381 * stopTime; // so that we can cancel the time updates 23382 * 23383 * // used to update the UI 23384 * function updateTime() { 23385 * element.text(dateFilter(new Date(), format)); 23386 * } 23387 * 23388 * // watch the expression, and update the UI on change. 23389 * scope.$watch(attrs.myCurrentTime, function(value) { 23390 * format = value; 23391 * updateTime(); 23392 * }); 23393 * 23394 * stopTime = $interval(updateTime, 1000); 23395 * 23396 * // listen on DOM destroy (removal) event, and cancel the next UI update 23397 * // to prevent updating time after the DOM element was removed. 23398 * element.on('$destroy', function() { 23399 * $interval.cancel(stopTime); 23400 * }); 23401 * } 23402 * }]); 23403 * </script> 23404 * 23405 * <div>
23406 * <div ng-controller="ExampleController"> 23407 * <label>Date format: <input ng-model="format"></label> <hr/> 23408 * Current time is: <span my-current-time="format"></span> 23409 * <hr/> 23410 * Blood 1 : <font color='red'>{{blood_1}}</font> 23411 * Blood 2 : <font color='red'>{{blood_2}}</font> 23412 * <button type="button" data-ng-click="fight()">Fight</button> 23413 * <button type="button" data-ng-click="stopFight()">StopFight</button> 23414 * <button type="button" data-ng-click="resetFight()">resetFight</button> 23415 * </div> 23416 * </div> 23417 * 23418 * </file> 23419 * </example> 23420 */ 23421 function interval(fn, delay, count, invokeApply) { 23422 var hasParams = arguments.length > 4, 23423 args = hasParams ? sliceArgs(arguments, 4) : [], 23424 setInterval = $window.setInterval, 23425 clearInterval = $window.clearInterval, 23426 iteration = 0, 23427 skipApply = (isDefined(invokeApply) && !invokeApply), 23428 deferred = (skipApply ? $$q : $q).defer(), 23429 promise = deferred.promise; 23430 23431 count = isDefined(count) ? count : 0; 23432 23433 promise.$$intervalId = setInterval(function tick() { 23434 if (skipApply) { 23435 $browser.defer(callback); 23436 } else { 23437 $rootScope.$evalAsync(callback); 23438 } 23439 deferred.notify(iteration++); 23440 23441 if (count > 0 && iteration >= count) { 23442 deferred.resolve(iteration); 23443 clearInterval(promise.$$intervalId); 23444 delete intervals[promise.$$intervalId]; 23445 } 23446 23447 if (!skipApply) $rootScope.$apply(); 23448 23449 }, delay); 23450 23451 intervals[promise.$$intervalId] = deferred; 23452 23453 return promise; 23454 23455 function callback() { 23456 if (!hasParams) { 23457 fn(iteration); 23458 } else { 23459 fn.apply(null, args); 23460 } 23461 } 23462 } 23463 23464 23465 /** 23466 * @ngdoc method 23467 * @name $interval#cancel 23468 * 23469 * @description 23470 * Cancels a task associated with the `promise`. 23471 * 23472 * @param {Promise=} promise returned by the `$interval` function. 23473 * @returns {boolean} Returns `true` if the task was successfully canceled. 23474 */ 23475 interval.cancel = function(promise) { 23476 if (promise && promise.$$intervalId in intervals) { 23477 // Interval cancels should not report as unhandled promise. 23478 intervals[promise.$$intervalId].promise.catch(noop); 23479 intervals[promise.$$intervalId].reject('canceled'); 23480 $window.clearInterval(promise.$$intervalId); 23481 delete intervals[promise.$$intervalId]; 23482 return true; 23483 } 23484 return false; 23485 }; 23486 23487 return interval; 23488 }]; 23489} 23490 23491/** 23492 * @ngdoc service 23493 * @name $jsonpCallbacks 23494 * @requires $window 23495 * @description 23496 * This service handles the lifecycle of callbacks to handle JSONP requests. 23497 * Override this service if you wish to customise where the callbacks are stored and 23498 * how they vary compared to the requested url. 23499 */ 23500var $jsonpCallbacksProvider = /** @this */ function() { 23501 this.$get = function() { 23502 var callbacks = angular.callbacks; 23503 var callbackMap = {}; 23504 23505 function createCallback(callbackId) { 23506 var callback = function(data) { 23507 callback.data = data; 23508 callback.called = true; 23509 }; 23510 callback.id = callbackId; 23511 return callback; 23512 } 23513 23514 return { 23515 /** 23516 * @ngdoc method 23517 * @name $jsonpCallbacks#createCallback 23518 * @param {string} url the url of the JSONP request 23519 * @returns {string} the callback path to send to the server as part of the JSONP request 23520 * @description 23521 * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback 23522 * to pass to the server, which will be used to call the callback with its payload in the JSONP response. 23523 */ 23524 createCallback: function(url) { 23525 var callbackId = '_' + (callbacks.$$counter++).toString(36); 23526 var callbackPath = 'angular.callbacks.' + callbackId; 23527 var callback = createCallback(callbackId); 23528 callbackMap[callbackPath] = callbacks[callbackId] = callback; 23529 return callbackPath; 23530 }, 23531 /** 23532 * @ngdoc method 23533 * @name $jsonpCallbacks#wasCalled 23534 * @param {string} callbackPath the path to the callback that was sent in the JSONP request 23535 * @returns {boolean} whether the callback has been called, as a result of the JSONP response
vendor: 13,793 bytes, lines 23536-24028
23536 * @description 23537 * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the 23538 * callback that was passed in the request. 23539 */ 23540 wasCalled: function(callbackPath) { 23541 return callbackMap[callbackPath].called; 23542 }, 23543 /** 23544 * @ngdoc method 23545 * @name $jsonpCallbacks#getResponse 23546 * @param {string} callbackPath the path to the callback that was sent in the JSONP request 23547 * @returns {*} the data received from the response via the registered callback 23548 * @description 23549 * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback 23550 * in the JSONP response. 23551 */ 23552 getResponse: function(callbackPath) { 23553 return callbackMap[callbackPath].data; 23554 }, 23555 /** 23556 * @ngdoc method 23557 * @name $jsonpCallbacks#removeCallback 23558 * @param {string} callbackPath the path to the callback that was sent in the JSONP request 23559 * @description 23560 * {@link $httpBackend} calls this method to remove the callback after the JSONP request has 23561 * completed or timed-out. 23562 */ 23563 removeCallback: function(callbackPath) { 23564 var callback = callbackMap[callbackPath]; 23565 delete callbacks[callback.id]; 23566 delete callbackMap[callbackPath]; 23567 } 23568 }; 23569 }; 23570}; 23571 23572/** 23573 * @ngdoc service 23574 * @name $locale 23575 * 23576 * @description 23577 * $locale service provides localization rules for various Angular components. As of right now the 23578 * only public api is: 23579 * 23580 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) 23581 */ 23582 23583var PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/, 23584 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; 23585var $locationMinErr = minErr('$location'); 23586 23587 23588/** 23589 * Encode path using encodeUriSegment, ignoring forward slashes 23590 * 23591 * @param {string} path Path to encode 23592 * @returns {string} 23593 */ 23594function encodePath(path) { 23595 var segments = path.split('/'), 23596 i = segments.length; 23597 23598 while (i--) { 23599 segments[i] = encodeUriSegment(segments[i]); 23600 } 23601 23602 return segments.join('/'); 23603} 23604 23605function parseAbsoluteUrl(absoluteUrl, locationObj) { 23606 var parsedUrl = urlResolve(absoluteUrl); 23607 23608 locationObj.$$protocol = parsedUrl.protocol; 23609 locationObj.$$host = parsedUrl.hostname; 23610 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; 23611} 23612 23613var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/; 23614function parseAppUrl(url, locationObj) { 23615 23616 if (DOUBLE_SLASH_REGEX.test(url)) { 23617 throw $locationMinErr('badpath', 'Invalid url "{0}".', url); 23618 } 23619 23620 var prefixed = (url.charAt(0) !== '/'); 23621 if (prefixed) { 23622 url = '/' + url; 23623 } 23624 var match = urlResolve(url); 23625 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? 23626 match.pathname.substring(1) : match.pathname); 23627 locationObj.$$search = parseKeyValue(match.search); 23628 locationObj.$$hash = decodeURIComponent(match.hash); 23629 23630 // make sure path starts with '/'; 23631 if (locationObj.$$path && locationObj.$$path.charAt(0) !== '/') { 23632 locationObj.$$path = '/' + locationObj.$$path; 23633 } 23634} 23635 23636function startsWith(str, search) { 23637 return str.slice(0, search.length) === search; 23638} 23639 23640/** 23641 * 23642 * @param {string} base 23643 * @param {string} url 23644 * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with 23645 * the expected string. 23646 */ 23647function stripBaseUrl(base, url) { 23648 if (startsWith(url, base)) { 23649 return url.substr(base.length); 23650 } 23651} 23652 23653 23654function stripHash(url) { 23655 var index = url.indexOf('#'); 23656 return index === -1 ? url : url.substr(0, index); 23657} 23658 23659function trimEmptyHash(url) { 23660 return url.replace(/(#.+)|#$/, '$1'); 23661} 23662 23663 23664function stripFile(url) { 23665 return url.substr(0, stripHash(url).lastIndexOf('/') + 1); 23666} 23667 23668/* return the server only (scheme://host:port) */ 23669function serverBase(url) { 23670 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); 23671} 23672 23673 23674/** 23675 * LocationHtml5Url represents a URL 23676 * This object is exposed as $location service when HTML5 mode is enabled and supported 23677 * 23678 * @constructor 23679 * @param {string} appBase application base URL 23680 * @param {string} appBaseNoFile application base URL stripped of any filename 23681 * @param {string} basePrefix URL path prefix 23682 */ 23683function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) { 23684 this.$$html5 = true; 23685 basePrefix = basePrefix || ''; 23686 parseAbsoluteUrl(appBase, this); 23687 23688 23689 /** 23690 * Parse given HTML5 (regular) URL string into properties 23691 * @param {string} url HTML5 URL 23692 * @private 23693 */ 23694 this.$$parse = function(url) { 23695 var pathUrl = stripBaseUrl(appBaseNoFile, url); 23696 if (!isString(pathUrl)) { 23697 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, 23698 appBaseNoFile); 23699 } 23700 23701 parseAppUrl(pathUrl, this); 23702 23703 if (!this.$$path) { 23704 this.$$path = '/'; 23705 } 23706 23707 this.$$compose(); 23708 }; 23709 23710 /** 23711 * Compose url and update `absUrl` property 23712 * @private 23713 */ 23714 this.$$compose = function() { 23715 var search = toKeyValue(this.$$search), 23716 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; 23717 23718 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; 23719 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' 23720 23721 this.$$urlUpdatedByLocation = true; 23722 }; 23723 23724 this.$$parseLinkUrl = function(url, relHref) { 23725 if (relHref && relHref[0] === '#') { 23726 // special case for links to hash fragments: 23727 // keep the old url and only replace the hash fragment 23728 this.hash(relHref.slice(1)); 23729 return true; 23730 } 23731 var appUrl, prevAppUrl; 23732 var rewrittenUrl; 23733 23734 23735 if (isDefined(appUrl = stripBaseUrl(appBase, url))) { 23736 prevAppUrl = appUrl; 23737 if (basePrefix && isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) { 23738 rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl); 23739 } else { 23740 rewrittenUrl = appBase + prevAppUrl; 23741 } 23742 } else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) { 23743 rewrittenUrl = appBaseNoFile + appUrl; 23744 } else if (appBaseNoFile === url + '/') { 23745 rewrittenUrl = appBaseNoFile; 23746 } 23747 if (rewrittenUrl) { 23748 this.$$parse(rewrittenUrl); 23749 } 23750 return !!rewrittenUrl; 23751 }; 23752} 23753 23754 23755/** 23756 * LocationHashbangUrl represents URL 23757 * This object is exposed as $location service when developer doesn't opt into html5 mode. 23758 * It also serves as the base class for html5 mode fallback on legacy browsers. 23759 * 23760 * @constructor 23761 * @param {string} appBase application base URL 23762 * @param {string} appBaseNoFile application base URL stripped of any filename 23763 * @param {string} hashPrefix hashbang prefix 23764 */ 23765function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { 23766 23767 parseAbsoluteUrl(appBase, this); 23768 23769 23770 /** 23771 * Parse given hashbang URL into properties 23772 * @param {string} url Hashbang URL 23773 * @private 23774 */ 23775 this.$$parse = function(url) { 23776 var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url); 23777 var withoutHashUrl; 23778 23779 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') { 23780 23781 // The rest of the URL starts with a hash so we have 23782 // got either a hashbang path or a plain hash fragment 23783 withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl); 23784 if (isUndefined(withoutHashUrl)) { 23785 // There was no hashbang prefix so we just have a hash fragment 23786 withoutHashUrl = withoutBaseUrl; 23787 } 23788 23789 } else { 23790 // There was no hashbang path nor hash fragment: 23791 // If we are in HTML5 mode we use what is left as the path; 23792 // Otherwise we ignore what is left 23793 if (this.$$html5) { 23794 withoutHashUrl = withoutBaseUrl; 23795 } else { 23796 withoutHashUrl = ''; 23797 if (isUndefined(withoutBaseUrl)) { 23798 appBase = url; 23799 /** @type {?} */ (this).replace(); 23800 } 23801 } 23802 } 23803 23804 parseAppUrl(withoutHashUrl, this); 23805 23806 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); 23807 23808 this.$$compose(); 23809 23810 /* 23811 * In Windows, on an anchor node on documents loaded from 23812 * the filesystem, the browser will return a pathname 23813 * prefixed with the drive name ('/C:/path') when a 23814 * pathname without a drive is set: 23815 * * a.setAttribute('href', '/foo') 23816 * * a.pathname === '/C:/foo' //true 23817 * 23818 * Inside of Angular, we're always using pathnames that 23819 * do not include drive names for routing. 23820 */ 23821 function removeWindowsDriveName(path, url, base) { 23822 /* 23823 Matches paths for file protocol on windows, 23824 such as /C:/foo/bar, and captures only /foo/bar. 23825 */ 23826 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; 23827 23828 var firstPathSegmentMatch; 23829 23830 //Get the relative path from the input URL. 23831 if (startsWith(url, base)) { 23832 url = url.replace(base, ''); 23833 } 23834 23835 // The input URL intentionally contains a first path segment that ends with a colon. 23836 if (windowsFilePathExp.exec(url)) { 23837 return path; 23838 } 23839 23840 firstPathSegmentMatch = windowsFilePathExp.exec(path); 23841 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; 23842 } 23843 }; 23844 23845 /** 23846 * Compose hashbang URL and update `absUrl` property 23847 * @private 23848 */ 23849 this.$$compose = function() { 23850 var search = toKeyValue(this.$$search), 23851 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; 23852 23853 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; 23854 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); 23855 23856 this.$$urlUpdatedByLocation = true; 23857 }; 23858 23859 this.$$parseLinkUrl = function(url, relHref) { 23860 if (stripHash(appBase) === stripHash(url)) { 23861 this.$$parse(url); 23862 return true; 23863 } 23864 return false; 23865 }; 23866} 23867 23868 23869/** 23870 * LocationHashbangUrl represents URL 23871 * This object is exposed as $location service when html5 history api is enabled but the browser 23872 * does not support it. 23873 * 23874 * @constructor 23875 * @param {string} appBase application base URL 23876 * @param {string} appBaseNoFile application base URL stripped of any filename 23877 * @param {string} hashPrefix hashbang prefix 23878 */ 23879function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) { 23880 this.$$html5 = true; 23881 LocationHashbangUrl.apply(this, arguments); 23882 23883 this.$$parseLinkUrl = function(url, relHref) { 23884 if (relHref && relHref[0] === '#') { 23885 // special case for links to hash fragments: 23886 // keep the old url and only replace the hash fragment 23887 this.hash(relHref.slice(1)); 23888 return true; 23889 } 23890 23891 var rewrittenUrl; 23892 var appUrl; 23893 23894 if (appBase === stripHash(url)) { 23895 rewrittenUrl = url; 23896 } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) { 23897 rewrittenUrl = appBase + hashPrefix + appUrl; 23898 } else if (appBaseNoFile === url + '/') { 23899 rewrittenUrl = appBaseNoFile; 23900 } 23901 if (rewrittenUrl) { 23902 this.$$parse(rewrittenUrl); 23903 } 23904 return !!rewrittenUrl; 23905 }; 23906 23907 this.$$compose = function() { 23908 var search = toKeyValue(this.$$search), 23909 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; 23910 23911 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; 23912 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#' 23913 this.$$absUrl = appBase + hashPrefix + this.$$url; 23914 23915 this.$$urlUpdatedByLocation = true; 23916 }; 23917 23918} 23919 23920 23921var locationPrototype = { 23922 23923 /** 23924 * Ensure absolute URL is initialized. 23925 * @private 23926 */ 23927 $$absUrl:'', 23928 23929 /** 23930 * Are we in html5 mode? 23931 * @private 23932 */ 23933 $$html5: false, 23934 23935 /** 23936 * Has any change been replacing? 23937 * @private 23938 */ 23939 $$replace: false, 23940 23941 /** 23942 * @ngdoc method 23943 * @name $location#absUrl 23944 * 23945 * @description 23946 * This method is getter only. 23947 * 23948 * Return full URL representation with all segments encoded according to rules specified in 23949 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). 23950 * 23951 * 23952 * ```js 23953 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo 23954 * var absUrl = $location.absUrl(); 23955 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" 23956 * ``` 23957 * 23958 * @return {string} full URL 23959 */ 23960 absUrl: locationGetter('$$absUrl'), 23961 23962 /** 23963 * @ngdoc method 23964 * @name $location#url 23965 * 23966 * @description 23967 * This method is getter / setter. 23968 * 23969 * Return URL (e.g. `/path?a=b#hash`) when called without any parameter. 23970 * 23971 * Change path, search and hash, when called with parameter and return `$location`. 23972 * 23973 * 23974 * ```js 23975 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo 23976 * var url = $location.url(); 23977 * // => "/some/path?foo=bar&baz=xoxo" 23978 * ``` 23979 * 23980 * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`) 23981 * @return {string} url 23982 */ 23983 url: function(url) { 23984 if (isUndefined(url)) { 23985 return this.$$url; 23986 } 23987 23988 var match = PATH_MATCH.exec(url); 23989 if (match[1] || url === '') this.path(decodeURIComponent(match[1])); 23990 if (match[2] || match[1] || url === '') this.search(match[3] || ''); 23991 this.hash(match[5] || ''); 23992 23993 return this; 23994 }, 23995 23996 /** 23997 * @ngdoc method 23998 * @name $location#protocol 23999 * 24000 * @description 24001 * This method is getter only. 24002 * 24003 * Return protocol of current URL. 24004 * 24005 * 24006 * ```js 24007 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo 24008 * var protocol = $location.protocol(); 24009 * // => "http" 24010 * ``` 24011 * 24012 * @return {string} protocol of current URL 24013 */ 24014 protocol: locationGetter('$$protocol'), 24015 24016 /** 24017 * @ngdoc method 24018 * @name $location#host 24019 * 24020 * @description 24021 * This method is getter only. 24022 * 24023 * Return host of current URL. 24024 * 24025 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only. 24026 * 24027 * 24028 * ```js
24029 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo 24030 * var host = $location.host(); 24031 * // => "example.com" 24032 * 24033 * // given URL http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo 24034 * host = $location.host(); 24035 * // => "example.com" 24036 * host = location.host; 24037 * // => "example.com:8080" 24038 * ``` 24039 * 24040 * @return {string} host of current URL. 24041 */ 24042 host: locationGetter('$$host'), 24043 24044 /** 24045 * @ngdoc method 24046 * @name $location#port 24047 * 24048 * @description 24049 * This method is getter only. 24050 * 24051 * Return port of current URL. 24052 * 24053 * 24054 * ```js 24055 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo 24056 * var port = $location.port(); 24057 * // => 80 24058 * ``` 24059 * 24060 * @return {Number} port 24061 */ 24062 port: locationGetter('$$port'), 24063 24064 /** 24065 * @ngdoc method 24066 * @name $location#path 24067 * 24068 * @description 24069 * This method is getter / setter. 24070 * 24071 * Return path of current URL when called without any parameter. 24072 * 24073 * Change path when called with parameter and return `$location`. 24074 * 24075 * Note: Path should always begin with forward slash (/), this method will add the forward slash 24076 * if it is missing. 24077 * 24078 * 24079 * ```js 24080 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo 24081 * var path = $location.path(); 24082 * // => "/some/path" 24083 * ``` 24084 * 24085 * @param {(string|number)=} path New path 24086 * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter 24087 */ 24088 path: locationGetterSetter('$$path', function(path) { 24089 path = path !== null ? path.toString() : ''; 24090 return path.charAt(0) === '/' ? path : '/' + path; 24091 }), 24092 24093 /** 24094 * @ngdoc method 24095 * @name $location#search 24096 * 24097 * @description 24098 * This method is getter / setter. 24099 * 24100 * Return search part (as object) of current URL when called without any parameter. 24101 * 24102 * Change search part when called with parameter and return `$location`. 24103 * 24104 * 24105 * ```js 24106 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo 24107 * var searchObject = $location.search(); 24108 * // => {foo: 'bar', baz: 'xoxo'} 24109 * 24110 * // set foo to 'yipee' 24111 * $location.search('foo', 'yipee'); 24112 * // $location.search() => {foo: 'yipee', baz: 'xoxo'} 24113 * ``` 24114 * 24115 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or 24116 * hash object. 24117 * 24118 * When called with a single argument the method acts as a setter, setting the `search` component 24119 * of `$location` to the specified value. 24120 * 24121 * If the argument is a hash object containing an array of values, these values will be encoded 24122 * as duplicate search parameters in the URL. 24123 * 24124 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue` 24125 * will override only a single search property. 24126 * 24127 * If `paramValue` is an array, it will override the property of the `search` component of 24128 * `$location` specified via the first argument. 24129 * 24130 * If `paramValue` is `null`, the property specified via the first argument will be deleted. 24131 * 24132 * If `paramValue` is `true`, the property specified via the first argument will be added with no 24133 * value nor trailing equal sign. 24134 * 24135 * @return {Object} If called with no arguments returns the parsed `search` object. If called with 24136 * one or more arguments returns `$location` object itself. 24137 */ 24138 search: function(search, paramValue) { 24139 switch (arguments.length) { 24140 case 0: 24141 return this.$$search; 24142 case 1: 24143 if (isString(search) || isNumber(search)) { 24144 search = search.toString(); 24145 this.$$search = parseKeyValue(search); 24146 } else if (isObject(search)) { 24147 search = copy(search, {}); 24148 // remove object undefined or null properties 24149 forEach(search, function(value, key) { 24150 if (value == null) delete search[key]; 24151 }); 24152 24153 this.$$search = search; 24154 } else { 24155 throw $locationMinErr('isrcharg', 24156 'The first argument of the `$location#search()` call must be a string or an object.'); 24157 } 24158 break; 24159 default: 24160 if (isUndefined(paramValue) || paramValue === null) { 24161 delete this.$$search[search]; 24162 } else { 24163 this.$$search[search] = paramValue; 24164 } 24165 } 24166 24167 this.$$compose(); 24168 return this; 24169 }, 24170 24171 /** 24172 * @ngdoc method 24173 * @name $location#hash 24174 * 24175 * @description 24176 * This method is getter / setter. 24177 * 24178 * Returns the hash fragment when called without any parameters. 24179 * 24180 * Changes the hash fragment when called with a parameter and returns `$location`. 24181 * 24182 * 24183 * ```js
24184 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue 24185 * var hash = $location.hash(); 24186 * // => "hashValue" 24187 * ``` 24188 * 24189 * @param {(string|number)=} hash New hash fragment 24190 * @return {string} hash 24191 */ 24192 hash: locationGetterSetter('$$hash', function(hash) { 24193 return hash !== null ? hash.toString() : ''; 24194 }), 24195 24196 /** 24197 * @ngdoc method 24198 * @name $location#replace 24199 * 24200 * @description 24201 * If called, all changes to $location during the current `$digest` will replace the current history 24202 * record, instead of adding a new one. 24203 */ 24204 replace: function() { 24205 this.$$replace = true; 24206 return this; 24207 } 24208}; 24209 24210forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) { 24211 Location.prototype = Object.create(locationPrototype); 24212 24213 /** 24214 * @ngdoc method 24215 * @name $location#state 24216 * 24217 * @description 24218 * This method is getter / setter. 24219 * 24220 * Return the history state object when called without any parameter. 24221 * 24222 * Change the history state object when called with one parameter and return `$location`. 24223 * The state object is later passed to `pushState` or `replaceState`. 24224 * 24225 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting 24226 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support 24227 * older browsers (like IE9 or Android < 4.0), don't use this method. 24228 * 24229 * @param {object=} state State object for pushState or replaceState 24230 * @return {object} state 24231 */ 24232 Location.prototype.state = function(state) { 24233 if (!arguments.length) { 24234 return this.$$state; 24235 } 24236 24237 if (Location !== LocationHtml5Url || !this.$$html5) { 24238 throw $locationMinErr('nostate', 'History API state support is available only ' + 24239 'in HTML5 mode and only in browsers supporting HTML5 History API'); 24240 } 24241 // The user might modify `stateObject` after invoking `$location.state(stateObject)` 24242 // but we're changing the $$state reference to $browser.state() during the $digest 24243 // so the modification window is narrow. 24244 this.$$state = isUndefined(state) ? null : state; 24245 this.$$urlUpdatedByLocation = true; 24246 24247 return this; 24248 }; 24249}); 24250 24251 24252function locationGetter(property) { 24253 return /** @this */ function() { 24254 return this[property]; 24255 }; 24256} 24257 24258 24259function locationGetterSetter(property, preprocess) { 24260 return /** @this */ function(value) { 24261 if (isUndefined(value)) { 24262 return this[property]; 24263 } 24264 24265 this[property] = preprocess(value); 24266 this.$$compose(); 24267 24268 return this; 24269 }; 24270} 24271 24272 24273/** 24274 * @ngdoc service 24275 * @name $location 24276 * 24277 * @requires $rootElement 24278 * 24279 * @description 24280 * The $location service parses the URL in the browser address bar (based on the 24281 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL 24282 * available to your application. Changes to the URL in the address bar are reflected into 24283 * $location service and changes to $location are reflected into the browser address bar. 24284 * 24285 * **The $location service:** 24286 * 24287 * - Exposes the current URL in the browser address bar, so you can 24288 * - Watch and observe the URL. 24289 * - Change the URL. 24290 * - Synchronizes the URL with the browser when the user 24291 * - Changes the address bar. 24292 * - Clicks the back or forward button (or clicks a History link). 24293 * - Clicks on a link. 24294 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). 24295 * 24296 * For more information see {@link guide/$location Developer Guide: Using $location} 24297 */ 24298 24299/** 24300 * @ngdoc provider 24301 * @name $locationProvider 24302 * @this 24303 * 24304 * @description 24305 * Use the `$locationProvider` to configure how the application deep linking paths are stored. 24306 */ 24307function $LocationProvider() { 24308 var hashPrefix = '!', 24309 html5Mode = { 24310 enabled: false, 24311 requireBase: true, 24312 rewriteLinks: true 24313 }; 24314 24315 /** 24316 * @ngdoc method 24317 * @name $locationProvider#hashPrefix 24318 * @description 24319 * The default value for the prefix is `'!'`. 24320 * @param {string=} prefix Prefix for hash part (containing path and search) 24321 * @returns {*} current value if used as getter or itself (chaining) if used as setter 24322 */ 24323 this.hashPrefix = function(prefix) { 24324 if (isDefined(prefix)) { 24325 hashPrefix = prefix; 24326 return this; 24327 } else { 24328 return hashPrefix; 24329 } 24330 }; 24331 24332 /** 24333 * @ngdoc method 24334 * @name $locationProvider#html5Mode 24335 * @description 24336 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value. 24337 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported 24338 * properties: 24339 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to 24340 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not 24341 * support `pushState`. 24342 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies 24343 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are 24344 * true, and a base tag is not present, an error will be thrown when `$location` is injected. 24345 * See the {@link guide/$location $location guide for more information} 24346 * - **rewriteLinks** - `{boolean|string}` - (default: `true`) When html5Mode is enabled, 24347 * enables/disables URL rewriting for relative links. If set to a string, URL rewriting will 24348 * only happen on links with an attribute that matches the given string. For example, if set 24349 * to `'internal-link'`, then the URL will only be rewritten for `<a internal-link>` links. 24350 * Note that [attribute name normalization](guide/directive#normalization) does not apply 24351 * here, so `'internalLink'` will **not** match `'internal-link'`. 24352 * 24353 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter 24354 */ 24355 this.html5Mode = function(mode) { 24356 if (isBoolean(mode)) { 24357 html5Mode.enabled = mode; 24358 return this; 24359 } else if (isObject(mode)) { 24360 24361 if (isBoolean(mode.enabled)) { 24362 html5Mode.enabled = mode.enabled; 24363 } 24364 24365 if (isBoolean(mode.requireBase)) { 24366 html5Mode.requireBase = mode.requireBase; 24367 } 24368 24369 if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) { 24370 html5Mode.rewriteLinks = mode.rewriteLinks; 24371 } 24372 24373 return this; 24374 } else { 24375 return html5Mode; 24376 } 24377 }; 24378 24379 /** 24380 * @ngdoc event 24381 * @name $location#$locationChangeStart 24382 * @eventType broadcast on root scope 24383 * @description 24384 * Broadcasted before a URL will change. 24385 * 24386 * This change can be prevented by calling 24387 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more 24388 * details about event object. Upon successful change 24389 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. 24390 * 24391 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when 24392 * the browser supports the HTML5 History API. 24393 * 24394 * @param {Object} angularEvent Synthetic event object. 24395 * @param {string} newUrl New URL 24396 * @param {string=} oldUrl URL that was before it was changed. 24397 * @param {string=} newState New history state object 24398 * @param {string=} oldState History state object that was before it was changed. 24399 */ 24400 24401 /** 24402 * @ngdoc event 24403 * @name $location#$locationChangeSuccess 24404 * @eventType broadcast on root scope 24405 * @description 24406 * Broadcasted after a URL was changed. 24407 * 24408 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when 24409 * the browser supports the HTML5 History API. 24410 * 24411 * @param {Object} angularEvent Synthetic event object. 24412 * @param {string} newUrl New URL 24413 * @param {string=} oldUrl URL that was before it was changed. 24414 * @param {string=} newState New history state object 24415 * @param {string=} oldState History state object that was before it was changed. 24416 */ 24417 24418 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window', 24419 function($rootScope, $browser, $sniffer, $rootElement, $window) { 24420 var $location, 24421 LocationMode, 24422 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' 24423 initialUrl = $browser.url(), 24424 appBase; 24425 24426 if (html5Mode.enabled) { 24427 if (!baseHref && html5Mode.requireBase) { 24428 throw $locationMinErr('nobase', 24429 '$location in HTML5 mode requires a <base> tag to be present!'); 24430 } 24431 appBase = serverBase(initialUrl) + (baseHref || '/'); 24432 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; 24433 } else { 24434 appBase = stripHash(initialUrl); 24435 LocationMode = LocationHashbangUrl; 24436 } 24437 var appBaseNoFile = stripFile(appBase); 24438
vendor: 9,011 bytes, lines 24439-24689
24439 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix); 24440 $location.$$parseLinkUrl(initialUrl, initialUrl); 24441 24442 $location.$$state = $browser.state(); 24443 24444 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; 24445 24446 function setBrowserUrlWithFallback(url, replace, state) { 24447 var oldUrl = $location.url(); 24448 var oldState = $location.$$state; 24449 try { 24450 $browser.url(url, replace, state); 24451 24452 // Make sure $location.state() returns referentially identical (not just deeply equal) 24453 // state object; this makes possible quick checking if the state changed in the digest 24454 // loop. Checking deep equality would be too expensive. 24455 $location.$$state = $browser.state(); 24456 } catch (e) { 24457 // Restore old values if pushState fails 24458 $location.url(oldUrl); 24459 $location.$$state = oldState; 24460 24461 throw e; 24462 } 24463 } 24464 24465 $rootElement.on('click', function(event) { 24466 var rewriteLinks = html5Mode.rewriteLinks; 24467 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) 24468 // currently we open nice url link and redirect then 24469 24470 if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return; 24471 24472 var elm = jqLite(event.target); 24473 24474 // traverse the DOM up to find first A tag 24475 while (nodeName_(elm[0]) !== 'a') { 24476 // ignore rewriting if no A tag (reached root element, or no parent - removed from document) 24477 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; 24478 } 24479 24480 if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return; 24481 24482 var absHref = elm.prop('href'); 24483 // get the actual href attribute - see 24484 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx 24485 var relHref = elm.attr('href') || elm.attr('xlink:href'); 24486 24487 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { 24488 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during 24489 // an animation. 24490 absHref = urlResolve(absHref.animVal).href; 24491 } 24492 24493 // Ignore when url is started with javascript: or mailto: 24494 if (IGNORE_URI_REGEXP.test(absHref)) return; 24495 24496 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) { 24497 if ($location.$$parseLinkUrl(absHref, relHref)) { 24498 // We do a preventDefault for all urls that are part of the angular application, 24499 // in html5mode and also without, so that we are able to abort navigation without 24500 // getting double entries in the location history. 24501 event.preventDefault(); 24502 // update location manually 24503 if ($location.absUrl() !== $browser.url()) { 24504 $rootScope.$apply(); 24505 // hack to work around FF6 bug 684208 when scenario runner clicks on links 24506 $window.angular['ff-684208-preventDefault'] = true; 24507 } 24508 } 24509 } 24510 }); 24511 24512 24513 // rewrite hashbang url <> html5 url 24514 if (trimEmptyHash($location.absUrl()) !== trimEmptyHash(initialUrl)) { 24515 $browser.url($location.absUrl(), true); 24516 } 24517 24518 var initializing = true; 24519 24520 // update $location when $browser url changes 24521 $browser.onUrlChange(function(newUrl, newState) { 24522 24523 if (!startsWith(newUrl, appBaseNoFile)) { 24524 // If we are navigating outside of the app then force a reload 24525 $window.location.href = newUrl; 24526 return; 24527 } 24528 24529 $rootScope.$evalAsync(function() { 24530 var oldUrl = $location.absUrl(); 24531 var oldState = $location.$$state; 24532 var defaultPrevented; 24533 newUrl = trimEmptyHash(newUrl); 24534 $location.$$parse(newUrl); 24535 $location.$$state = newState; 24536 24537 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, 24538 newState, oldState).defaultPrevented; 24539 24540 // if the location was changed by a `$locationChangeStart` handler then stop 24541 // processing this location change 24542 if ($location.absUrl() !== newUrl) return; 24543 24544 if (defaultPrevented) { 24545 $location.$$parse(oldUrl); 24546 $location.$$state = oldState; 24547 setBrowserUrlWithFallback(oldUrl, false, oldState); 24548 } else { 24549 initializing = false; 24550 afterLocationChange(oldUrl, oldState); 24551 } 24552 }); 24553 if (!$rootScope.$$phase) $rootScope.$digest(); 24554 }); 24555 24556 // update browser 24557 $rootScope.$watch(function $locationWatch() { 24558 if (initializing || $location.$$urlUpdatedByLocation) { 24559 $location.$$urlUpdatedByLocation = false; 24560 24561 var oldUrl = trimEmptyHash($browser.url()); 24562 var newUrl = trimEmptyHash($location.absUrl()); 24563 var oldState = $browser.state(); 24564 var currentReplace = $location.$$replace; 24565 var urlOrStateChanged = oldUrl !== newUrl || 24566 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); 24567 24568 if (initializing || urlOrStateChanged) { 24569 initializing = false; 24570 24571 $rootScope.$evalAsync(function() { 24572 var newUrl = $location.absUrl(); 24573 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, 24574 $location.$$state, oldState).defaultPrevented; 24575 24576 // if the location was changed by a `$locationChangeStart` handler then stop 24577 // processing this location change 24578 if ($location.absUrl() !== newUrl) return; 24579 24580 if (defaultPrevented) { 24581 $location.$$parse(oldUrl); 24582 $location.$$state = oldState; 24583 } else { 24584 if (urlOrStateChanged) { 24585 setBrowserUrlWithFallback(newUrl, currentReplace, 24586 oldState === $location.$$state ? null : $location.$$state); 24587 } 24588 afterLocationChange(oldUrl, oldState); 24589 } 24590 }); 24591 } 24592 } 24593 24594 $location.$$replace = false; 24595 24596 // we don't need to return anything because $evalAsync will make the digest loop dirty when 24597 // there is a change 24598 }); 24599 24600 return $location; 24601 24602 function afterLocationChange(oldUrl, oldState) { 24603 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl, 24604 $location.$$state, oldState); 24605 } 24606}]; 24607} 24608 24609/** 24610 * @ngdoc service 24611 * @name $log 24612 * @requires $window 24613 * 24614 * @description 24615 * Simple service for logging. Default implementation safely writes the message 24616 * into the browser's console (if present). 24617 * 24618 * The main purpose of this service is to simplify debugging and troubleshooting. 24619 * 24620 * The default is to log `debug` messages. You can use 24621 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. 24622 * 24623 * @example 24624 <example module="logExample" name="log-service"> 24625 <file name="script.js"> 24626 angular.module('logExample', []) 24627 .controller('LogController', ['$scope', '$log', function($scope, $log) { 24628 $scope.$log = $log; 24629 $scope.message = 'Hello World!'; 24630 }]); 24631 </file> 24632 <file name="index.html"> 24633 <div ng-controller="LogController"> 24634 <p>Reload this page with open console, enter text and hit the log button...</p> 24635 <label>Message: 24636 <input type="text" ng-model="message" /></label> 24637 <button ng-click="$log.log(message)">log</button> 24638 <button ng-click="$log.warn(message)">warn</button> 24639 <button ng-click="$log.info(message)">info</button> 24640 <button ng-click="$log.error(message)">error</button> 24641 <button ng-click="$log.debug(message)">debug</button> 24642 </div> 24643 </file> 24644 </example> 24645 */ 24646 24647/** 24648 * @ngdoc provider 24649 * @name $logProvider 24650 * @this 24651 * 24652 * @description 24653 * Use the `$logProvider` to configure how the application logs messages 24654 */ 24655function $LogProvider() { 24656 var debug = true, 24657 self = this; 24658 24659 /** 24660 * @ngdoc method 24661 * @name $logProvider#debugEnabled 24662 * @description 24663 * @param {boolean=} flag enable or disable debug level messages 24664 * @returns {*} current value if used as getter or itself (chaining) if used as setter 24665 */ 24666 this.debugEnabled = function(flag) { 24667 if (isDefined(flag)) { 24668 debug = flag; 24669 return this; 24670 } else { 24671 return debug; 24672 } 24673 }; 24674 24675 this.$get = ['$window', function($window) { 24676 // Support: IE 9-11, Edge 12-14+ 24677 // IE/Edge display errors in such a way that it requires the user to click in 4 places 24678 // to see the stack trace. There is no way to feature-detect it so there's a chance 24679 // of the user agent sniffing to go wrong but since it's only about logging, this shouldn't 24680 // break apps. Other browsers display errors in a sensible way and some of them map stack 24681 // traces along source maps if available so it makes sense to let browsers display it 24682 // as they want. 24683 var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent); 24684 24685 return { 24686 /** 24687 * @ngdoc method 24688 * @name $log#log 24689 *
24690 * @description 24691 * Write a log message 24692 */ 24693 log: consoleLog('log'), 24694 24695 /** 24696 * @ngdoc method 24697 * @name $log#info 24698 * 24699 * @description 24700 * Write an information message 24701 */ 24702 info: consoleLog('info'), 24703 24704 /** 24705 * @ngdoc method 24706 * @name $log#warn 24707 * 24708 * @description 24709 * Write a warning message 24710 */ 24711 warn: consoleLog('warn'), 24712 24713 /** 24714 * @ngdoc method 24715 * @name $log#error 24716 * 24717 * @description 24718 * Write an error message 24719 */ 24720 error: consoleLog('error'), 24721 24722 /** 24723 * @ngdoc method 24724 * @name $log#debug 24725 * 24726 * @description 24727 * Write a debug message 24728 */ 24729 debug: (function() { 24730 var fn = consoleLog('debug'); 24731 24732 return function() { 24733 if (debug) { 24734 fn.apply(self, arguments); 24735 } 24736 }; 24737 })() 24738 }; 24739 24740 function formatError(arg) { 24741 if (arg instanceof Error) { 24742 if (arg.stack && formatStackTrace) { 24743 arg = (arg.message && arg.stack.indexOf(arg.message) === -1) 24744 ? 'Error: ' + arg.message + '\n' + arg.stack 24745 : arg.stack; 24746 } else if (arg.sourceURL) { 24747 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; 24748 } 24749 } 24750 return arg; 24751 } 24752 24753 function consoleLog(type) { 24754 var console = $window.console || {}, 24755 logFn = console[type] || console.log || noop, 24756 hasApply = false; 24757 24758 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. 24759 // The reason behind this is that console.log has type "object" in IE8... 24760 try { 24761 hasApply = !!logFn.apply; 24762 } catch (e) { /* empty */ } 24763 24764 if (hasApply) { 24765 return function() { 24766 var args = []; 24767 forEach(arguments, function(arg) { 24768 args.push(formatError(arg)); 24769 }); 24770 return logFn.apply(console, args); 24771 }; 24772 } 24773 24774 // we are IE which either doesn't have window.console => this is noop and we do nothing, 24775 // or we are IE where console.log doesn't have apply so we log at least first 2 args 24776 return function(arg1, arg2) { 24777 logFn(arg1, arg2 == null ? '' : arg2); 24778 }; 24779 } 24780 }]; 24781} 24782 24783/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 24784 * Any commits to this file should be reviewed with security in mind. * 24785 * Changes to this file can potentially create security vulnerabilities. * 24786 * An approval from 2 Core members with history of modifying * 24787 * this file is required. * 24788 * * 24789 * Does the change somehow allow for arbitrary javascript to be executed? * 24790 * Or allows for someone to change the prototype of built-in objects? * 24791 * Or gives undesired access to variables likes document or window? * 24792 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 24793 24794var $parseMinErr = minErr('$parse'); 24795 24796var objectValueOf = {}.constructor.prototype.valueOf; 24797 24798// Sandboxing Angular Expressions 24799// ------------------------------ 24800// Angular expressions are no longer sandboxed. So it is now even easier to access arbitrary JS code by 24801// various means such as obtaining a reference to native JS functions like the Function constructor. 24802// 24803// As an example, consider the following Angular
vendor: 17,378 bytes, lines 24803-25404
24803 expression: 24804// 24805// {}.toString.constructor('alert("evil JS code")') 24806// 24807// It is important to realize that if you create an expression from a string that contains user provided 24808// content then it is possible that your application contains a security vulnerability to an XSS style attack. 24809// 24810// See https://docs.angularjs.org/guide/security 24811 24812 24813function getStringValue(name) { 24814 // Property names must be strings. This means that non-string objects cannot be used 24815 // as keys in an object. Any non-string object, including a number, is typecasted 24816 // into a string via the toString method. 24817 // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names 24818 // 24819 // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it 24820 // to a string. It's not always possible. If `name` is an object and its `toString` method is 24821 // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown: 24822 // 24823 // TypeError: Cannot convert object to primitive value 24824 // 24825 // For performance reasons, we don't catch this error here and allow it to propagate up the call 24826 // stack. Note that you'll get the same error in JavaScript if you try to access a property using 24827 // such a 'broken' object as a key. 24828 return name + ''; 24829} 24830 24831 24832var OPERATORS = createMap(); 24833forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; }); 24834var ESCAPE = {'n':'\n', 'f':'\f', 'r':'\r', 't':'\t', 'v':'\v', '\'':'\'', '"':'"'}; 24835 24836 24837///////////////////////////////////////// 24838 24839 24840/** 24841 * @constructor 24842 */ 24843var Lexer = function Lexer(options) { 24844 this.options = options; 24845}; 24846 24847Lexer.prototype = { 24848 constructor: Lexer, 24849 24850 lex: function(text) { 24851 this.text = text; 24852 this.index = 0; 24853 this.tokens = []; 24854 24855 while (this.index < this.text.length) { 24856 var ch = this.text.charAt(this.index); 24857 if (ch === '"' || ch === '\'') { 24858 this.readString(ch); 24859 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { 24860 this.readNumber(); 24861 } else if (this.isIdentifierStart(this.peekMultichar())) { 24862 this.readIdent(); 24863 } else if (this.is(ch, '(){}[].,;:?')) { 24864 this.tokens.push({index: this.index, text: ch}); 24865 this.index++; 24866 } else if (this.isWhitespace(ch)) { 24867 this.index++; 24868 } else { 24869 var ch2 = ch + this.peek(); 24870 var ch3 = ch2 + this.peek(2); 24871 var op1 = OPERATORS[ch]; 24872 var op2 = OPERATORS[ch2]; 24873 var op3 = OPERATORS[ch3]; 24874 if (op1 || op2 || op3) { 24875 var token = op3 ? ch3 : (op2 ? ch2 : ch); 24876 this.tokens.push({index: this.index, text: token, operator: true}); 24877 this.index += token.length; 24878 } else { 24879 this.throwError('Unexpected next character ', this.index, this.index + 1); 24880 } 24881 } 24882 } 24883 return this.tokens; 24884 }, 24885 24886 is: function(ch, chars) { 24887 return chars.indexOf(ch) !== -1; 24888 }, 24889 24890 peek: function(i) { 24891 var num = i || 1; 24892 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; 24893 }, 24894 24895 isNumber: function(ch) { 24896 return ('0' <= ch && ch <= '9') && typeof ch === 'string'; 24897 }, 24898 24899 isWhitespace: function(ch) { 24900 // IE treats non-breaking space as \u00A0 24901 return (ch === ' ' || ch === '\r' || ch === '\t' || 24902 ch === '\n' || ch === '\v' || ch === '\u00A0'); 24903 }, 24904 24905 isIdentifierStart: function(ch) { 24906 return this.options.isIdentifierStart ? 24907 this.options.isIdentifierStart(ch, this.codePointAt(ch)) : 24908 this.isValidIdentifierStart(ch); 24909 }, 24910 24911 isValidIdentifierStart: function(ch) { 24912 return ('a' <= ch && ch <= 'z' || 24913 'A' <= ch && ch <= 'Z' || 24914 '_' === ch || ch === '$'); 24915 }, 24916 24917 isIdentifierContinue: function(ch) { 24918 return this.options.isIdentifierContinue ? 24919 this.options.isIdentifierContinue(ch, this.codePointAt(ch)) : 24920 this.isValidIdentifierContinue(ch); 24921 }, 24922 24923 isValidIdentifierContinue: function(ch, cp) { 24924 return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch); 24925 }, 24926 24927 codePointAt: function(ch) { 24928 if (ch.length === 1) return ch.charCodeAt(0); 24929 // eslint-disable-next-line no-bitwise 24930 return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00; 24931 }, 24932 24933 peekMultichar: function() { 24934 var ch = this.text.charAt(this.index); 24935 var peek = this.peek(); 24936 if (!peek) { 24937 return ch; 24938 } 24939 var cp1 = ch.charCodeAt(0); 24940 var cp2 = peek.charCodeAt(0); 24941 if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) { 24942 return ch + peek; 24943 } 24944 return ch; 24945 }, 24946 24947 isExpOperator: function(ch) { 24948 return (ch === '-' || ch === '+' || this.isNumber(ch)); 24949 }, 24950 24951 throwError: function(error, start, end) { 24952 end = end || this.index; 24953 var colStr = (isDefined(start) 24954 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' 24955 : ' ' + end); 24956 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', 24957 error, colStr, this.text); 24958 }, 24959 24960 readNumber: function() { 24961 var number = ''; 24962 var start = this.index; 24963 while (this.index < this.text.length) { 24964 var ch = lowercase(this.text.charAt(this.index)); 24965 if (ch === '.' || this.isNumber(ch)) { 24966 number += ch; 24967 } else { 24968 var peekCh = this.peek(); 24969 if (ch === 'e' && this.isExpOperator(peekCh)) { 24970 number += ch; 24971 } else if (this.isExpOperator(ch) && 24972 peekCh && this.isNumber(peekCh) && 24973 number.charAt(number.length - 1) === 'e') { 24974 number += ch; 24975 } else if (this.isExpOperator(ch) && 24976 (!peekCh || !this.isNumber(peekCh)) && 24977 number.charAt(number.length - 1) === 'e') { 24978 this.throwError('Invalid exponent'); 24979 } else { 24980 break; 24981 } 24982 } 24983 this.index++; 24984 } 24985 this.tokens.push({ 24986 index: start, 24987 text: number, 24988 constant: true, 24989 value: Number(number) 24990 }); 24991 }, 24992 24993 readIdent: function() { 24994 var start = this.index; 24995 this.index += this.peekMultichar().length; 24996 while (this.index < this.text.length) { 24997 var ch = this.peekMultichar(); 24998 if (!this.isIdentifierContinue(ch)) { 24999 break; 25000 } 25001 this.index += ch.length; 25002 } 25003 this.tokens.push({ 25004 index: start, 25005 text: this.text.slice(start, this.index), 25006 identifier: true 25007 }); 25008 }, 25009 25010 readString: function(quote) { 25011 var start = this.index; 25012 this.index++; 25013 var string = ''; 25014 var rawString = quote; 25015 var escape = false; 25016 while (this.index < this.text.length) { 25017 var ch = this.text.charAt(this.index); 25018 rawString += ch; 25019 if (escape) { 25020 if (ch === 'u') { 25021 var hex = this.text.substring(this.index + 1, this.index + 5); 25022 if (!hex.match(/[\da-f]{4}/i)) { 25023 this.throwError('Invalid unicode escape [\\u' + hex + ']'); 25024 } 25025 this.index += 4; 25026 string += String.fromCharCode(parseInt(hex, 16)); 25027 } else { 25028 var rep = ESCAPE[ch]; 25029 string = string + (rep || ch); 25030 } 25031 escape = false; 25032 } else if (ch === '\\') { 25033 escape = true; 25034 } else if (ch === quote) { 25035 this.index++; 25036 this.tokens.push({ 25037 index: start, 25038 text: rawString, 25039 constant: true, 25040 value: string 25041 }); 25042 return; 25043 } else { 25044 string += ch; 25045 } 25046 this.index++; 25047 } 25048 this.throwError('Unterminated quote', start); 25049 } 25050}; 25051 25052var AST = function AST(lexer, options) { 25053 this.lexer = lexer; 25054 this.options = options; 25055}; 25056 25057AST.Program = 'Program'; 25058AST.ExpressionStatement = 'ExpressionStatement'; 25059AST.AssignmentExpression = 'AssignmentExpression'; 25060AST.ConditionalExpression = 'ConditionalExpression'; 25061AST.LogicalExpression = 'LogicalExpression'; 25062AST.BinaryExpression = 'BinaryExpression'; 25063AST.UnaryExpression = 'UnaryExpression'; 25064AST.CallExpression = 'CallExpression'; 25065AST.MemberExpression = 'MemberExpression'; 25066AST.Identifier = 'Identifier'; 25067AST.Literal = 'Literal'; 25068AST.ArrayExpression = 'ArrayExpression'; 25069AST.Property = 'Property'; 25070AST.ObjectExpression = 'ObjectExpression'; 25071AST.ThisExpression = 'ThisExpression'; 25072AST.LocalsExpression = 'LocalsExpression'; 25073 25074// Internal use only 25075AST.NGValueParameter = 'NGValueParameter'; 25076 25077AST.prototype = { 25078 ast: function(text) { 25079 this.text = text; 25080 this.tokens = this.lexer.lex(text); 25081 25082 var value = this.program(); 25083 25084 if (this.tokens.length !== 0) { 25085 this.throwError('is an unexpected token', this.tokens[0]); 25086 } 25087 25088 return value; 25089 }, 25090 25091 program: function() { 25092 var body = []; 25093 while (true) { 25094 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) 25095 body.push(this.expressionStatement()); 25096 if (!this.expect(';')) { 25097 return { type: AST.Program, body: body}; 25098 } 25099 } 25100 }, 25101 25102 expressionStatement: function() { 25103 return { type: AST.ExpressionStatement, expression: this.filterChain() }; 25104 }, 25105 25106 filterChain: function() { 25107 var left = this.expression(); 25108 while (this.expect('|')) { 25109 left = this.filter(left); 25110 } 25111 return left; 25112 }, 25113 25114 expression: function() { 25115 return this.assignment(); 25116 }, 25117 25118 assignment: function() { 25119 var result = this.ternary(); 25120 if (this.expect('=')) { 25121 if (!isAssignable(result)) { 25122 throw $parseMinErr('lval', 'Trying to assign a value to a non l-value'); 25123 } 25124 25125 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='}; 25126 } 25127 return result; 25128 }, 25129 25130 ternary: function() { 25131 var test = this.logicalOR(); 25132 var alternate; 25133 var consequent; 25134 if (this.expect('?')) { 25135 alternate = this.expression(); 25136 if (this.consume(':')) { 25137 consequent = this.expression(); 25138 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent}; 25139 } 25140 } 25141 return test; 25142 }, 25143 25144 logicalOR: function() { 25145 var left = this.logicalAND(); 25146 while (this.expect('||')) { 25147 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() }; 25148 } 25149 return left; 25150 }, 25151 25152 logicalAND: function() { 25153 var left = this.equality(); 25154 while (this.expect('&&')) { 25155 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()}; 25156 } 25157 return left; 25158 }, 25159 25160 equality: function() { 25161 var left = this.relational(); 25162 var token; 25163 while ((token = this.expect('==','!=','===','!=='))) { 25164 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() }; 25165 } 25166 return left; 25167 }, 25168 25169 relational: function() { 25170 var left = this.additive(); 25171 var token; 25172 while ((token = this.expect('<', '>', '<=', '>='))) { 25173 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() }; 25174 } 25175 return left; 25176 }, 25177 25178 additive: function() { 25179 var left = this.multiplicative(); 25180 var token; 25181 while ((token = this.expect('+','-'))) { 25182 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() }; 25183 } 25184 return left; 25185 }, 25186 25187 multiplicative: function() { 25188 var left = this.unary(); 25189 var token; 25190 while ((token = this.expect('*','/','%'))) { 25191 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() }; 25192 } 25193 return left; 25194 }, 25195 25196 unary: function() { 25197 var token; 25198 if ((token = this.expect('+', '-', '!'))) { 25199 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() }; 25200 } else { 25201 return this.primary(); 25202 } 25203 }, 25204 25205 primary: function() { 25206 var primary; 25207 if (this.expect('(')) { 25208 primary = this.filterChain(); 25209 this.consume(')'); 25210 } else if (this.expect('[')) { 25211 primary = this.arrayDeclaration(); 25212 } else if (this.expect('{')) { 25213 primary = this.object(); 25214 } else if (this.selfReferential.hasOwnProperty(this.peek().text)) { 25215 primary = copy(this.selfReferential[this.consume().text]); 25216 } else if (this.options.literals.hasOwnProperty(this.peek().text)) { 25217 primary = { type: AST.Literal, value: this.options.literals[this.consume().text]}; 25218 } else if (this.peek().identifier) { 25219 primary = this.identifier(); 25220 } else if (this.peek().constant) { 25221 primary = this.constant(); 25222 } else { 25223 this.throwError('not a primary expression', this.peek()); 25224 } 25225 25226 var next; 25227 while ((next = this.expect('(', '[', '.'))) { 25228 if (next.text === '(') { 25229 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() }; 25230 this.consume(')'); 25231 } else if (next.text === '[') { 25232 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true }; 25233 this.consume(']'); 25234 } else if (next.text === '.') { 25235 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false }; 25236 } else { 25237 this.throwError('IMPOSSIBLE'); 25238 } 25239 } 25240 return primary; 25241 }, 25242 25243 filter: function(baseExpression) { 25244 var args = [baseExpression]; 25245 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true}; 25246 25247 while (this.expect(':')) { 25248 args.push(this.expression()); 25249 } 25250 25251 return result; 25252 }, 25253 25254 parseArguments: function() { 25255 var args = []; 25256 if (this.peekToken().text !== ')') { 25257 do { 25258 args.push(this.filterChain()); 25259 } while (this.expect(',')); 25260 } 25261 return args; 25262 }, 25263 25264 identifier: function() { 25265 var token = this.consume(); 25266 if (!token.identifier) { 25267 this.throwError('is not a valid identifier', token); 25268 } 25269 return { type: AST.Identifier, name: token.text }; 25270 }, 25271 25272 constant: function() { 25273 // TODO check that it is a constant 25274 return { type: AST.Literal, value: this.consume().value }; 25275 }, 25276 25277 arrayDeclaration: function() { 25278 var elements = []; 25279 if (this.peekToken().text !== ']') { 25280 do { 25281 if (this.peek(']')) { 25282 // Support trailing commas per ES5.1. 25283 break; 25284 } 25285 elements.push(this.expression()); 25286 } while (this.expect(',')); 25287 } 25288 this.consume(']'); 25289 25290 return { type: AST.ArrayExpression, elements: elements }; 25291 }, 25292 25293 object: function() { 25294 var properties = [], property; 25295 if (this.peekToken().text !== '}') { 25296 do { 25297 if (this.peek('}')) { 25298 // Support trailing commas per ES5.1. 25299 break; 25300 } 25301 property = {type: AST.Property, kind: 'init'}; 25302 if (this.peek().constant) { 25303 property.key = this.constant(); 25304 property.computed = false; 25305 this.consume(':'); 25306 property.value = this.expression(); 25307 } else if (this.peek().identifier) { 25308 property.key = this.identifier(); 25309 property.computed = false; 25310 if (this.peek(':')) { 25311 this.consume(':'); 25312 property.value = this.expression(); 25313 } else { 25314 property.value = property.key; 25315 } 25316 } else if (this.peek('[')) { 25317 this.consume('['); 25318 property.key = this.expression(); 25319 this.consume(']'); 25320 property.computed = true; 25321 this.consume(':'); 25322 property.value = this.expression(); 25323 } else { 25324 this.throwError('invalid key', this.peek()); 25325 } 25326 properties.push(property); 25327 } while (this.expect(',')); 25328 } 25329 this.consume('}'); 25330 25331 return {type: AST.ObjectExpression, properties: properties }; 25332 }, 25333 25334 throwError: function(msg, token) { 25335 throw $parseMinErr('syntax', 25336 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', 25337 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); 25338 }, 25339 25340 consume: function(e1) { 25341 if (this.tokens.length === 0) { 25342 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); 25343 } 25344 25345 var token = this.expect(e1); 25346 if (!token) { 25347 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); 25348 } 25349 return token; 25350 }, 25351 25352 peekToken: function() { 25353 if (this.tokens.length === 0) { 25354 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); 25355 } 25356 return this.tokens[0]; 25357 }, 25358 25359 peek: function(e1, e2, e3, e4) { 25360 return this.peekAhead(0, e1, e2, e3, e4); 25361 }, 25362 25363 peekAhead: function(i, e1, e2, e3, e4) { 25364 if (this.tokens.length > i) { 25365 var token = this.tokens[i]; 25366 var t = token.text; 25367 if (t === e1 || t === e2 || t === e3 || t === e4 || 25368 (!e1 && !e2 && !e3 && !e4)) { 25369 return token; 25370 } 25371 } 25372 return false; 25373 }, 25374 25375 expect: function(e1, e2, e3, e4) { 25376 var token = this.peek(e1, e2, e3, e4); 25377 if (token) { 25378 this.tokens.shift(); 25379 return token; 25380 } 25381 return false; 25382 }, 25383 25384 selfReferential: { 25385 'this': {type: AST.ThisExpression }, 25386 '$locals': {type: AST.LocalsExpression } 25387 } 25388}; 25389 25390function ifDefined(v, d) { 25391 return typeof v !== 'undefined' ? v : d; 25392} 25393 25394function plusFn(l, r) { 25395 if (typeof l === 'undefined') return r; 25396 if (typeof r === 'undefined') return l; 25397 return l + r; 25398} 25399 25400function isStateless($filter, filterName) { 25401 var fn = $filter(filterName); 25402 return !fn.$stateful; 25403} 25404
vendor: 7,330 bytes, lines 25405-25632
25405function findConstantAndWatchExpressions(ast, $filter) { 25406 var allConstants; 25407 var argsToWatch; 25408 var isStatelessFilter; 25409 switch (ast.type) { 25410 case AST.Program: 25411 allConstants = true; 25412 forEach(ast.body, function(expr) { 25413 findConstantAndWatchExpressions(expr.expression, $filter); 25414 allConstants = allConstants && expr.expression.constant; 25415 }); 25416 ast.constant = allConstants; 25417 break; 25418 case AST.Literal: 25419 ast.constant = true; 25420 ast.toWatch = []; 25421 break; 25422 case AST.UnaryExpression: 25423 findConstantAndWatchExpressions(ast.argument, $filter); 25424 ast.constant = ast.argument.constant; 25425 ast.toWatch = ast.argument.toWatch; 25426 break; 25427 case AST.BinaryExpression: 25428 findConstantAndWatchExpressions(ast.left, $filter); 25429 findConstantAndWatchExpressions(ast.right, $filter); 25430 ast.constant = ast.left.constant && ast.right.constant; 25431 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch); 25432 break; 25433 case AST.LogicalExpression: 25434 findConstantAndWatchExpressions(ast.left, $filter); 25435 findConstantAndWatchExpressions(ast.right, $filter); 25436 ast.constant = ast.left.constant && ast.right.constant; 25437 ast.toWatch = ast.constant ? [] : [ast]; 25438 break; 25439 case AST.ConditionalExpression: 25440 findConstantAndWatchExpressions(ast.test, $filter); 25441 findConstantAndWatchExpressions(ast.alternate, $filter); 25442 findConstantAndWatchExpressions(ast.consequent, $filter); 25443 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant; 25444 ast.toWatch = ast.constant ? [] : [ast]; 25445 break; 25446 case AST.Identifier: 25447 ast.constant = false; 25448 ast.toWatch = [ast]; 25449 break; 25450 case AST.MemberExpression: 25451 findConstantAndWatchExpressions(ast.object, $filter); 25452 if (ast.computed) { 25453 findConstantAndWatchExpressions(ast.property, $filter); 25454 } 25455 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant); 25456 ast.toWatch = [ast]; 25457 break; 25458 case AST.CallExpression: 25459 isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false; 25460 allConstants = isStatelessFilter; 25461 argsToWatch = []; 25462 forEach(ast.arguments, function(expr) { 25463 findConstantAndWatchExpressions(expr, $filter); 25464 allConstants = allConstants && expr.constant; 25465 if (!expr.constant) { 25466 argsToWatch.push.apply(argsToWatch, expr.toWatch); 25467 } 25468 }); 25469 ast.constant = allConstants; 25470 ast.toWatch = isStatelessFilter ? argsToWatch : [ast]; 25471 break; 25472 case AST.AssignmentExpression: 25473 findConstantAndWatchExpressions(ast.left, $filter); 25474 findConstantAndWatchExpressions(ast.right, $filter); 25475 ast.constant = ast.left.constant && ast.right.constant; 25476 ast.toWatch = [ast]; 25477 break; 25478 case AST.ArrayExpression: 25479 allConstants = true; 25480 argsToWatch = []; 25481 forEach(ast.elements, function(expr) { 25482 findConstantAndWatchExpressions(expr, $filter); 25483 allConstants = allConstants && expr.constant; 25484 if (!expr.constant) { 25485 argsToWatch.push.apply(argsToWatch, expr.toWatch); 25486 } 25487 }); 25488 ast.constant = allConstants; 25489 ast.toWatch = argsToWatch; 25490 break; 25491 case AST.ObjectExpression: 25492 allConstants = true; 25493 argsToWatch = []; 25494 forEach(ast.properties, function(property) { 25495 findConstantAndWatchExpressions(property.value, $filter); 25496 allConstants = allConstants && property.value.constant && !property.computed; 25497 if (!property.value.constant) { 25498 argsToWatch.push.apply(argsToWatch, property.value.toWatch); 25499 } 25500 if (property.computed) { 25501 findConstantAndWatchExpressions(property.key, $filter); 25502 if (!property.key.constant) { 25503 argsToWatch.push.apply(argsToWatch, property.key.toWatch); 25504 } 25505 } 25506 25507 }); 25508 ast.constant = allConstants; 25509 ast.toWatch = argsToWatch; 25510 break; 25511 case AST.ThisExpression: 25512 ast.constant = false; 25513 ast.toWatch = []; 25514 break; 25515 case AST.LocalsExpression: 25516 ast.constant = false; 25517 ast.toWatch = []; 25518 break; 25519 } 25520} 25521 25522function getInputs(body) { 25523 if (body.length !== 1) return; 25524 var lastExpression = body[0].expression; 25525 var candidate = lastExpression.toWatch; 25526 if (candidate.length !== 1) return candidate; 25527 return candidate[0] !== lastExpression ? candidate : undefined; 25528} 25529 25530function isAssignable(ast) { 25531 return ast.type === AST.Identifier || ast.type === AST.MemberExpression; 25532} 25533 25534function assignableAST(ast) { 25535 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) { 25536 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='}; 25537 } 25538} 25539 25540function isLiteral(ast) { 25541 return ast.body.length === 0 || 25542 ast.body.length === 1 && ( 25543 ast.body[0].expression.type === AST.Literal || 25544 ast.body[0].expression.type === AST.ArrayExpression || 25545 ast.body[0].expression.type === AST.ObjectExpression); 25546} 25547 25548function isConstant(ast) { 25549 return ast.constant; 25550} 25551 25552function ASTCompiler(astBuilder, $filter) { 25553 this.astBuilder = astBuilder; 25554 this.$filter = $filter; 25555} 25556 25557ASTCompiler.prototype = { 25558 compile: function(expression) { 25559 var self = this; 25560 var ast = this.astBuilder.ast(expression); 25561 this.state = { 25562 nextId: 0, 25563 filters: {}, 25564 fn: {vars: [], body: [], own: {}}, 25565 assign: {vars: [], body: [], own: {}}, 25566 inputs: [] 25567 }; 25568 findConstantAndWatchExpressions(ast, self.$filter); 25569 var extra = ''; 25570 var assignable; 25571 this.stage = 'assign'; 25572 if ((assignable = assignableAST(ast))) { 25573 this.state.computing = 'assign'; 25574 var result = this.nextId(); 25575 this.recurse(assignable, result); 25576 this.return_(result); 25577 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l'); 25578 } 25579 var toWatch = getInputs(ast.body); 25580 self.stage = 'inputs'; 25581 forEach(toWatch, function(watch, key) { 25582 var fnKey = 'fn' + key; 25583 self.state[fnKey] = {vars: [], body: [], own: {}}; 25584 self.state.computing = fnKey; 25585 var intoId = self.nextId(); 25586 self.recurse(watch, intoId); 25587 self.return_(intoId); 25588 self.state.inputs.push(fnKey); 25589 watch.watchId = key; 25590 }); 25591 this.state.computing = 'fn'; 25592 this.stage = 'main'; 25593 this.recurse(ast); 25594 var fnString = 25595 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex. 25596 // This is a workaround for this until we do a better job at only removing the prefix only when we should. 25597 '"' + this.USE + ' ' + this.STRICT + '";\n' + 25598 this.filterPrefix() + 25599 'var fn=' + this.generateFunction('fn', 's,l,a,i') + 25600 extra + 25601 this.watchFns() + 25602 'return fn;'; 25603 25604 // eslint-disable-next-line no-new-func 25605 var fn = (new Function('$filter', 25606 'getStringValue', 25607 'ifDefined', 25608 'plus', 25609 fnString))( 25610 this.$filter, 25611 getStringValue, 25612 ifDefined, 25613 plusFn); 25614 this.state = this.stage = undefined; 25615 fn.literal = isLiteral(ast); 25616 fn.constant = isConstant(ast); 25617 return fn; 25618 }, 25619 25620 USE: 'use', 25621 25622 STRICT: 'strict', 25623 25624 watchFns: function() { 25625 var result = []; 25626 var fns = this.state.inputs; 25627 var self = this; 25628 forEach(fns, function(name) { 25629 result.push('var ' + name + '=' + self.generateFunction(name, 's')); 25630 }); 25631 if (fns.length) { 25632 result.push('fn.inputs=[' + fns
vendor: 12,657 bytes, lines 25632-26019
25632.join(',') + '];'); 25633 } 25634 return result.join(''); 25635 }, 25636 25637 generateFunction: function(name, params) { 25638 return 'function(' + params + '){' + 25639 this.varsPrefix(name) + 25640 this.body(name) + 25641 '};'; 25642 }, 25643 25644 filterPrefix: function() { 25645 var parts = []; 25646 var self = this; 25647 forEach(this.state.filters, function(id, filter) { 25648 parts.push(id + '=$filter(' + self.escape(filter) + ')'); 25649 }); 25650 if (parts.length) return 'var ' + parts.join(',') + ';'; 25651 return ''; 25652 }, 25653 25654 varsPrefix: function(section) { 25655 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : ''; 25656 }, 25657 25658 body: function(section) { 25659 return this.state[section].body.join(''); 25660 }, 25661 25662 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { 25663 var left, right, self = this, args, expression, computed; 25664 recursionFn = recursionFn || noop; 25665 if (!skipWatchIdCheck && isDefined(ast.watchId)) { 25666 intoId = intoId || this.nextId(); 25667 this.if_('i', 25668 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)), 25669 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true) 25670 ); 25671 return; 25672 } 25673 switch (ast.type) { 25674 case AST.Program: 25675 forEach(ast.body, function(expression, pos) { 25676 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; }); 25677 if (pos !== ast.body.length - 1) { 25678 self.current().body.push(right, ';'); 25679 } else { 25680 self.return_(right); 25681 } 25682 }); 25683 break; 25684 case AST.Literal: 25685 expression = this.escape(ast.value); 25686 this.assign(intoId, expression); 25687 recursionFn(intoId || expression); 25688 break; 25689 case AST.UnaryExpression: 25690 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; }); 25691 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')'; 25692 this.assign(intoId, expression); 25693 recursionFn(expression); 25694 break; 25695 case AST.BinaryExpression: 25696 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; }); 25697 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; }); 25698 if (ast.operator === '+') { 25699 expression = this.plus(left, right); 25700 } else if (ast.operator === '-') { 25701 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0); 25702 } else { 25703 expression = '(' + left + ')' + ast.operator + '(' + right + ')'; 25704 } 25705 this.assign(intoId, expression); 25706 recursionFn(expression); 25707 break; 25708 case AST.LogicalExpression: 25709 intoId = intoId || this.nextId(); 25710 self.recurse(ast.left, intoId); 25711 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId)); 25712 recursionFn(intoId); 25713 break; 25714 case AST.ConditionalExpression: 25715 intoId = intoId || this.nextId(); 25716 self.recurse(ast.test, intoId); 25717 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId)); 25718 recursionFn(intoId); 25719 break; 25720 case AST.Identifier: 25721 intoId = intoId || this.nextId(); 25722 if (nameId) { 25723 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s'); 25724 nameId.computed = false; 25725 nameId.name = ast.name; 25726 } 25727 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)), 25728 function() { 25729 self.if_(self.stage === 'inputs' || 's', function() { 25730 if (create && create !== 1) { 25731 self.if_( 25732 self.isNull(self.nonComputedMember('s', ast.name)), 25733 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}')); 25734 } 25735 self.assign(intoId, self.nonComputedMember('s', ast.name)); 25736 }); 25737 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name)) 25738 ); 25739 recursionFn(intoId); 25740 break; 25741 case AST.MemberExpression: 25742 left = nameId && (nameId.context = this.nextId()) || this.nextId(); 25743 intoId = intoId || this.nextId(); 25744 self.recurse(ast.object, left, undefined, function() { 25745 self.if_(self.notNull(left), function() { 25746 if (ast.computed) { 25747 right = self.nextId(); 25748 self.recurse(ast.property, right); 25749 self.getStringValue(right); 25750 if (create && create !== 1) { 25751 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}')); 25752 } 25753 expression = self.computedMember(left, right); 25754 self.assign(intoId, expression); 25755 if (nameId) { 25756 nameId.computed = true; 25757 nameId.name = right; 25758 } 25759 } else { 25760 if (create && create !== 1) { 25761 self.if_(self.isNull(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}')); 25762 } 25763 expression = self.nonComputedMember(left, ast.property.name); 25764 self.assign(intoId, expression); 25765 if (nameId) { 25766 nameId.computed = false; 25767 nameId.name = ast.property.name; 25768 } 25769 } 25770 }, function() { 25771 self.assign(intoId, 'undefined'); 25772 }); 25773 recursionFn(intoId); 25774 }, !!create); 25775 break; 25776 case AST.CallExpression: 25777 intoId = intoId || this.nextId(); 25778 if (ast.filter) { 25779 right = self.filter(ast.callee.name); 25780 args = []; 25781 forEach(ast.arguments, function(expr) { 25782 var argument = self.nextId(); 25783 self.recurse(expr, argument); 25784 args.push(argument); 25785 }); 25786 expression = right + '(' + args.join(',') + ')'; 25787 self.assign(intoId, expression); 25788 recursionFn(intoId); 25789 } else { 25790 right = self.nextId(); 25791 left = {}; 25792 args = []; 25793 self.recurse(ast.callee, right, left, function() { 25794 self.if_(self.notNull(right), function() { 25795 forEach(ast.arguments, function(expr) { 25796 self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) { 25797 args.push(argument); 25798 }); 25799 }); 25800 if (left.name) { 25801 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')'; 25802 } else { 25803 expression = right + '(' + args.join(',') + ')'; 25804 } 25805 self.assign(intoId, expression); 25806 }, function() { 25807 self.assign(intoId, 'undefined'); 25808 }); 25809 recursionFn(intoId); 25810 }); 25811 } 25812 break; 25813 case AST.AssignmentExpression: 25814 right = this.nextId(); 25815 left = {}; 25816 this.recurse(ast.left, undefined, left, function() { 25817 self.if_(self.notNull(left.context), function() { 25818 self.recurse(ast.right, right); 25819 expression = self.member(left.context, left.name, left.computed) + ast.operator + right; 25820 self.assign(intoId, expression); 25821 recursionFn(intoId || expression); 25822 }); 25823 }, 1); 25824 break; 25825 case AST.ArrayExpression: 25826 args = []; 25827 forEach(ast.elements, function(expr) { 25828 self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) { 25829 args.push(argument); 25830 }); 25831 }); 25832 expression = '[' + args.join(',') + ']'; 25833 this.assign(intoId, expression); 25834 recursionFn(intoId || expression); 25835 break; 25836 case AST.ObjectExpression: 25837 args = []; 25838 computed = false; 25839 forEach(ast.properties, function(property) { 25840 if (property.computed) { 25841 computed = true; 25842 } 25843 }); 25844 if (computed) { 25845 intoId = intoId || this.nextId(); 25846 this.assign(intoId, '{}'); 25847 forEach(ast.properties, function(property) { 25848 if (property.computed) { 25849 left = self.nextId(); 25850 self.recurse(property.key, left); 25851 } else { 25852 left = property.key.type === AST.Identifier ? 25853 property.key.name : 25854 ('' + property.key.value); 25855 } 25856 right = self.nextId(); 25857 self.recurse(property.value, right); 25858 self.assign(self.member(intoId, left, property.computed), right); 25859 }); 25860 } else { 25861 forEach(ast.properties, function(property) { 25862 self.recurse(property.value, ast.constant ? undefined : self.nextId(), undefined, function(expr) { 25863 args.push(self.escape( 25864 property.key.type === AST.Identifier ? property.key.name : 25865 ('' + property.key.value)) + 25866 ':' + expr); 25867 }); 25868 }); 25869 expression = '{' + args.join(',') + '}'; 25870 this.assign(intoId, expression); 25871 } 25872 recursionFn(intoId || expression); 25873 break; 25874 case AST.ThisExpression: 25875 this.assign(intoId, 's'); 25876 recursionFn(intoId || 's'); 25877 break; 25878 case AST.LocalsExpression: 25879 this.assign(intoId, 'l'); 25880 recursionFn(intoId || 'l'); 25881 break; 25882 case AST.NGValueParameter: 25883 this.assign(intoId, 'v'); 25884 recursionFn(intoId || 'v'); 25885 break; 25886 } 25887 }, 25888 25889 getHasOwnProperty: function(element, property) { 25890 var key = element + '.' + property; 25891 var own = this.current().own; 25892 if (!own.hasOwnProperty(key)) { 25893 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')'); 25894 } 25895 return own[key]; 25896 }, 25897 25898 assign: function(id, value) { 25899 if (!id) return; 25900 this.current().body.push(id, '=', value, ';'); 25901 return id; 25902 }, 25903 25904 filter: function(filterName) { 25905 if (!this.state.filters.hasOwnProperty(filterName)) { 25906 this.state.filters[filterName] = this.nextId(true); 25907 } 25908 return this.state.filters[filterName]; 25909 }, 25910 25911 ifDefined: function(id, defaultValue) { 25912 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')'; 25913 }, 25914 25915 plus: function(left, right) { 25916 return 'plus(' + left + ',' + right + ')'; 25917 }, 25918 25919 return_: function(id) { 25920 this.current().body.push('return ', id, ';'); 25921 }, 25922 25923 if_: function(test, alternate, consequent) { 25924 if (test === true) { 25925 alternate(); 25926 } else { 25927 var body = this.current().body; 25928 body.push('if(', test, '){'); 25929 alternate(); 25930 body.push('}'); 25931 if (consequent) { 25932 body.push('else{'); 25933 consequent(); 25934 body.push('}'); 25935 } 25936 } 25937 }, 25938 25939 not: function(expression) { 25940 return '!(' + expression + ')'; 25941 }, 25942 25943 isNull: function(expression) { 25944 return expression + '==null'; 25945 }, 25946 25947 notNull: function(expression) { 25948 return expression + '!=null'; 25949 }, 25950 25951 nonComputedMember: function(left, right) { 25952 var SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/; 25953 var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g; 25954 if (SAFE_IDENTIFIER.test(right)) { 25955 return left + '.' + right; 25956 } else { 25957 return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]'; 25958 } 25959 }, 25960 25961 computedMember: function(left, right) { 25962 return left + '[' + right + ']'; 25963 }, 25964 25965 member: function(left, right, computed) { 25966 if (computed) return this.computedMember(left, right); 25967 return this.nonComputedMember(left, right); 25968 }, 25969 25970 getStringValue: function(item) { 25971 this.assign(item, 'getStringValue(' + item + ')'); 25972 }, 25973 25974 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { 25975 var self = this; 25976 return function() { 25977 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck); 25978 }; 25979 }, 25980 25981 lazyAssign: function(id, value) { 25982 var self = this; 25983 return function() { 25984 self.assign(id, value); 25985 }; 25986 }, 25987 25988 stringEscapeRegex: /[^ a-zA-Z0-9]/g, 25989 25990 stringEscapeFn: function(c) { 25991 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); 25992 }, 25993 25994 escape: function(value) { 25995 if (isString(value)) return '\'' + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + '\''; 25996 if (isNumber(value)) return value.toString(); 25997 if (value === true) return 'true'; 25998 if (value === false) return 'false'; 25999 if (value === null) return 'null'; 26000 if (typeof value === 'undefined') return 'undefined'; 26001 26002 throw $parseMinErr('esc', 'IMPOSSIBLE'); 26003 }, 26004 26005 nextId: function(skip, init) { 26006 var id = 'v' + (this.state.nextId++); 26007 if (!skip) { 26008 this.current().vars.push(id + (init ? '=' + init : '')); 26009 } 26010 return id; 26011 }, 26012 26013 current: function() { 26014 return this.state[this.state.computing]; 26015 } 26016}; 26017 26018 26019function ASTInterpreter(
vendor: 1,403 bytes, lines 26019-26067
26019astBuilder, $filter) { 26020 this.astBuilder = astBuilder; 26021 this.$filter = $filter; 26022} 26023 26024ASTInterpreter.prototype = { 26025 compile: function(expression) { 26026 var self = this; 26027 var ast = this.astBuilder.ast(expression); 26028 findConstantAndWatchExpressions(ast, self.$filter); 26029 var assignable; 26030 var assign; 26031 if ((assignable = assignableAST(ast))) { 26032 assign = this.recurse(assignable); 26033 } 26034 var toWatch = getInputs(ast.body); 26035 var inputs; 26036 if (toWatch) { 26037 inputs = []; 26038 forEach(toWatch, function(watch, key) { 26039 var input = self.recurse(watch); 26040 watch.input = input; 26041 inputs.push(input); 26042 watch.watchId = key; 26043 }); 26044 } 26045 var expressions = []; 26046 forEach(ast.body, function(expression) { 26047 expressions.push(self.recurse(expression.expression)); 26048 }); 26049 var fn = ast.body.length === 0 ? noop : 26050 ast.body.length === 1 ? expressions[0] : 26051 function(scope, locals) { 26052 var lastValue; 26053 forEach(expressions, function(exp) { 26054 lastValue = exp(scope, locals); 26055 }); 26056 return lastValue; 26057 }; 26058 if (assign) { 26059 fn.assign = function(scope, value, locals) { 26060 return assign(scope, locals, value); 26061 }; 26062 } 26063 if (inputs) { 26064 fn.inputs = inputs; 26065 } 26066 fn.literal = isLiteral(ast); 26067 fn.constant = isConstant(ast);
vendor: 11,669 bytes, lines 26067-26395
26067 26068 return fn; 26069 }, 26070 26071 recurse: function(ast, context, create) { 26072 var left, right, self = this, args; 26073 if (ast.input) { 26074 return this.inputs(ast.input, ast.watchId); 26075 } 26076 switch (ast.type) { 26077 case AST.Literal: 26078 return this.value(ast.value, context); 26079 case AST.UnaryExpression: 26080 right = this.recurse(ast.argument); 26081 return this['unary' + ast.operator](right, context); 26082 case AST.BinaryExpression: 26083 left = this.recurse(ast.left); 26084 right = this.recurse(ast.right); 26085 return this['binary' + ast.operator](left, right, context); 26086 case AST.LogicalExpression: 26087 left = this.recurse(ast.left); 26088 right = this.recurse(ast.right); 26089 return this['binary' + ast.operator](left, right, context); 26090 case AST.ConditionalExpression: 26091 return this['ternary?:']( 26092 this.recurse(ast.test), 26093 this.recurse(ast.alternate), 26094 this.recurse(ast.consequent), 26095 context 26096 ); 26097 case AST.Identifier: 26098 return self.identifier(ast.name, context, create); 26099 case AST.MemberExpression: 26100 left = this.recurse(ast.object, false, !!create); 26101 if (!ast.computed) { 26102 right = ast.property.name; 26103 } 26104 if (ast.computed) right = this.recurse(ast.property); 26105 return ast.computed ? 26106 this.computedMember(left, right, context, create) : 26107 this.nonComputedMember(left, right, context, create); 26108 case AST.CallExpression: 26109 args = []; 26110 forEach(ast.arguments, function(expr) { 26111 args.push(self.recurse(expr)); 26112 }); 26113 if (ast.filter) right = this.$filter(ast.callee.name); 26114 if (!ast.filter) right = this.recurse(ast.callee, true); 26115 return ast.filter ? 26116 function(scope, locals, assign, inputs) { 26117 var values = []; 26118 for (var i = 0; i < args.length; ++i) { 26119 values.push(args[i](scope, locals, assign, inputs)); 26120 } 26121 var value = right.apply(undefined, values, inputs); 26122 return context ? {context: undefined, name: undefined, value: value} : value; 26123 } : 26124 function(scope, locals, assign, inputs) { 26125 var rhs = right(scope, locals, assign, inputs); 26126 var value; 26127 if (rhs.value != null) { 26128 var values = []; 26129 for (var i = 0; i < args.length; ++i) { 26130 values.push(args[i](scope, locals, assign, inputs)); 26131 } 26132 value = rhs.value.apply(rhs.context, values); 26133 } 26134 return context ? {value: value} : value; 26135 }; 26136 case AST.AssignmentExpression: 26137 left = this.recurse(ast.left, true, 1); 26138 right = this.recurse(ast.right); 26139 return function(scope, locals, assign, inputs) { 26140 var lhs = left(scope, locals, assign, inputs); 26141 var rhs = right(scope, locals, assign, inputs); 26142 lhs.context[lhs.name] = rhs; 26143 return context ? {value: rhs} : rhs; 26144 }; 26145 case AST.ArrayExpression: 26146 args = []; 26147 forEach(ast.elements, function(expr) { 26148 args.push(self.recurse(expr)); 26149 }); 26150 return function(scope, locals, assign, inputs) { 26151 var value = []; 26152 for (var i = 0; i < args.length; ++i) { 26153 value.push(args[i](scope, locals, assign, inputs)); 26154 } 26155 return context ? {value: value} : value; 26156 }; 26157 case AST.ObjectExpression: 26158 args = []; 26159 forEach(ast.properties, function(property) { 26160 if (property.computed) { 26161 args.push({key: self.recurse(property.key), 26162 computed: true, 26163 value: self.recurse(property.value) 26164 }); 26165 } else { 26166 args.push({key: property.key.type === AST.Identifier ? 26167 property.key.name : 26168 ('' + property.key.value), 26169 computed: false, 26170 value: self.recurse(property.value) 26171 }); 26172 } 26173 }); 26174 return function(scope, locals, assign, inputs) { 26175 var value = {}; 26176 for (var i = 0; i < args.length; ++i) { 26177 if (args[i].computed) { 26178 value[args[i].key(scope, locals, assign, inputs)] = args[i].value(scope, locals, assign, inputs); 26179 } else { 26180 value[args[i].key] = args[i].value(scope, locals, assign, inputs); 26181 } 26182 } 26183 return context ? {value: value} : value; 26184 }; 26185 case AST.ThisExpression: 26186 return function(scope) { 26187 return context ? {value: scope} : scope; 26188 }; 26189 case AST.LocalsExpression: 26190 return function(scope, locals) { 26191 return context ? {value: locals} : locals; 26192 }; 26193 case AST.NGValueParameter: 26194 return function(scope, locals, assign) { 26195 return context ? {value: assign} : assign; 26196 }; 26197 } 26198 }, 26199 26200 'unary+': function(argument, context) { 26201 return function(scope, locals, assign, inputs) { 26202 var arg = argument(scope, locals, assign, inputs); 26203 if (isDefined(arg)) { 26204 arg = +arg; 26205 } else { 26206 arg = 0; 26207 } 26208 return context ? {value: arg} : arg; 26209 }; 26210 }, 26211 'unary-': function(argument, context) { 26212 return function(scope, locals, assign, inputs) { 26213 var arg = argument(scope, locals, assign, inputs); 26214 if (isDefined(arg)) { 26215 arg = -arg; 26216 } else { 26217 arg = -0; 26218 } 26219 return context ? {value: arg} : arg; 26220 }; 26221 }, 26222 'unary!': function(argument, context) { 26223 return function(scope, locals, assign, inputs) { 26224 var arg = !argument(scope, locals, assign, inputs); 26225 return context ? {value: arg} : arg; 26226 }; 26227 }, 26228 'binary+': function(left, right, context) { 26229 return function(scope, locals, assign, inputs) { 26230 var lhs = left(scope, locals, assign, inputs); 26231 var rhs = right(scope, locals, assign, inputs); 26232 var arg = plusFn(lhs, rhs); 26233 return context ? {value: arg} : arg; 26234 }; 26235 }, 26236 'binary-': function(left, right, context) { 26237 return function(scope, locals, assign, inputs) { 26238 var lhs = left(scope, locals, assign, inputs); 26239 var rhs = right(scope, locals, assign, inputs); 26240 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0); 26241 return context ? {value: arg} : arg; 26242 }; 26243 }, 26244 'binary*': function(left, right, context) { 26245 return function(scope, locals, assign, inputs) { 26246 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs); 26247 return context ? {value: arg} : arg; 26248 }; 26249 }, 26250 'binary/': function(left, right, context) { 26251 return function(scope, locals, assign, inputs) { 26252 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs); 26253 return context ? {value: arg} : arg; 26254 }; 26255 }, 26256 'binary%': function(left, right, context) { 26257 return function(scope, locals, assign, inputs) { 26258 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs); 26259 return context ? {value: arg} : arg; 26260 }; 26261 }, 26262 'binary===': function(left, right, context) { 26263 return function(scope, locals, assign, inputs) { 26264 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs); 26265 return context ? {value: arg} : arg; 26266 }; 26267 }, 26268 'binary!==': function(left, right, context) { 26269 return function(scope, locals, assign, inputs) { 26270 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs); 26271 return context ? {value: arg} : arg; 26272 }; 26273 }, 26274 'binary==': function(left, right, context) { 26275 return function(scope, locals, assign, inputs) { 26276 // eslint-disable-next-line eqeqeq 26277 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs); 26278 return context ? {value: arg} : arg; 26279 }; 26280 }, 26281 'binary!=': function(left, right, context) { 26282 return function(scope, locals, assign, inputs) { 26283 // eslint-disable-next-line eqeqeq 26284 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs); 26285 return context ? {value: arg} : arg; 26286 }; 26287 }, 26288 'binary<': function(left, right, context) { 26289 return function(scope, locals, assign, inputs) { 26290 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs); 26291 return context ? {value: arg} : arg; 26292 }; 26293 }, 26294 'binary>': function(left, right, context) { 26295 return function(scope, locals, assign, inputs) { 26296 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs); 26297 return context ? {value: arg} : arg; 26298 }; 26299 }, 26300 'binary<=': function(left, right, context) { 26301 return function(scope, locals, assign, inputs) { 26302 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs); 26303 return context ? {value: arg} : arg; 26304 }; 26305 }, 26306 'binary>=': function(left, right, context) { 26307 return function(scope, locals, assign, inputs) { 26308 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs); 26309 return context ? {value: arg} : arg; 26310 }; 26311 }, 26312 'binary&&': function(left, right, context) { 26313 return function(scope, locals, assign, inputs) { 26314 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs); 26315 return context ? {value: arg} : arg; 26316 }; 26317 }, 26318 'binary||': function(left, right, context) { 26319 return function(scope, locals, assign, inputs) { 26320 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs); 26321 return context ? {value: arg} : arg; 26322 }; 26323 }, 26324 'ternary?:': function(test, alternate, consequent, context) { 26325 return function(scope, locals, assign, inputs) { 26326 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs); 26327 return context ? {value: arg} : arg; 26328 }; 26329 }, 26330 value: function(value, context) { 26331 return function() { return context ? {context: undefined, name: undefined, value: value} : value; }; 26332 }, 26333 identifier: function(name, context, create) { 26334 return function(scope, locals, assign, inputs) { 26335 var base = locals && (name in locals) ? locals : scope; 26336 if (create && create !== 1 && base && base[name] == null) { 26337 base[name] = {}; 26338 } 26339 var value = base ? base[name] : undefined; 26340 if (context) { 26341 return {context: base, name: name, value: value}; 26342 } else { 26343 return value; 26344 } 26345 }; 26346 }, 26347 computedMember: function(left, right, context, create) { 26348 return function(scope, locals, assign, inputs) { 26349 var lhs = left(scope, locals, assign, inputs); 26350 var rhs; 26351 var value; 26352 if (lhs != null) { 26353 rhs = right(scope, locals, assign, inputs); 26354 rhs = getStringValue(rhs); 26355 if (create && create !== 1) { 26356 if (lhs && !(lhs[rhs])) { 26357 lhs[rhs] = {}; 26358 } 26359 } 26360 value = lhs[rhs]; 26361 } 26362 if (context) { 26363 return {context: lhs, name: rhs, value: value}; 26364 } else { 26365 return value; 26366 } 26367 }; 26368 }, 26369 nonComputedMember: function(left, right, context, create) { 26370 return function(scope, locals, assign, inputs) { 26371 var lhs = left(scope, locals, assign, inputs); 26372 if (create && create !== 1) { 26373 if (lhs && lhs[right] == null) { 26374 lhs[right] = {}; 26375 } 26376 } 26377 var value = lhs != null ? lhs[right] : undefined; 26378 if (context) { 26379 return {context: lhs, name: right, value: value}; 26380 } else { 26381 return value; 26382 } 26383 }; 26384 }, 26385 inputs: function(input, watchId) { 26386 return function(scope, value, locals, inputs) { 26387 if (inputs) return inputs[watchId]; 26388 return input(scope, value, locals); 26389 }; 26390 } 26391}; 26392 26393/** 26394 * @constructor 26395 */
26396var Parser = function Parser(lexer, $filter, options) { 26397 this.lexer = lexer; 26398 this.$filter = $filter; 26399 this.options = options; 26400 this.ast = new AST(lexer, options); 26401 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) : 26402 new ASTCompiler(this.ast, $filter); 26403}; 26404 26405Parser.prototype = { 26406 constructor: Parser, 26407 26408 parse: function(text) { 26409 return this.astCompiler.compile(text); 26410 } 26411}; 26412 26413function getValueOf(value) { 26414 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value); 26415} 26416 26417/////////////////////////////////// 26418 26419/** 26420 * @ngdoc service 26421 * @name $parse 26422 * @kind function 26423 * 26424 * @description 26425 * 26426 * Converts Angular {@link guide/expression expression} into a function. 26427 * 26428 * ```js 26429 * var getter = $parse('user.name'); 26430 * var setter = getter.assign; 26431 * var context = {user:{name:'angular'}}; 26432 * var locals = {user:{name:'local'}}; 26433 * 26434 * expect(getter(context)).toEqual('angular'); 26435 * setter(context, 'newValue'); 26436 * expect(context.user.name).toEqual('newValue'); 26437 * expect(getter(context, locals)).toEqual('local'); 26438 * ``` 26439 * 26440 * 26441 * @param {string} expression String expression to compile. 26442 * @returns {function(context, locals)} a function which represents the compiled expression: 26443 * 26444 * * `context` – `{object}` – an object against which any expressions embedded in the strings 26445 * are evaluated against (typically a scope object). 26446 * * `locals` – `{object=}` – local variables context object, useful for overriding values in 26447 * `context`. 26448 * 26449 * The returned function also has the following properties: 26450 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript 26451 * literal. 26452 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript 26453 * constant literals. 26454 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be 26455 * set to a function to change its value on the given context. 26456 * 26457 */ 26458 26459 26460/** 26461 * @ngdoc provider 26462 * @name $parseProvider 26463 * @this 26464 * 26465 * @description 26466 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} 26467 * service. 26468 */ 26469function $ParseProvider() { 26470 var cache = createMap(); 26471 var literals = { 26472 'true': true, 26473 'false': false, 26474 'null': null, 26475 'undefined': undefined 26476 }; 26477 var identStart, identContinue; 26478 26479 /** 26480 * @ngdoc method 26481 * @name $parseProvider#addLiteral 26482 * @description 26483 * 26484 * Configure $parse service to add literal values that will be present as literal at expressions. 26485 * 26486 * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name. 26487 * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`. 26488 * 26489 **/ 26490 this.addLiteral = function(literalName, literalValue) { 26491 literals[literalName] = literalValue; 26492 }; 26493 26494 /** 26495 * @ngdoc method 26496 * @name $parseProvider#setIdentifierFns 26497 * 26498 * @description 26499 * 26500 * Allows defining the set of characters that are allowed in Angular expressions. The function 26501 * `identifierStart` will get called to know if a given character is a valid character to be the 26502 * first character for an identifier. The function `identifierContinue` will get called to know if 26503 * a given character is a valid character to be a follow-up identifier character. The functions 26504 * `identifierStart` and `identifierContinue` will receive as arguments the single character to be 26505 * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in 26506 * mind that the `string` parameter can be two characters long depending on the character 26507 * representation. It is expected for the function to return `true` or `false`, whether that 26508 * character is allowed or not. 26509 * 26510 * Since this function will be called extensively, keep the implementation of these functions fast, 26511 * as the performance of these functions have a direct impact on the expressions parsing speed. 26512 * 26513 * @param {function=} identifierStart The function that will decide whether the given character is 26514 * a valid identifier start character. 26515 * @param {function=} identifierContinue The function that will decide whether the given character is 26516 * a valid identifier continue character. 26517 */ 26518 this.setIdentifierFns = function(identifierStart, identifierContinue) { 26519 identStart = identifierStart; 26520 identContinue = identifierContinue; 26521 return this; 26522 }; 26523 26524 this.$get = ['$filter', function($filter) { 26525 var noUnsafeEval = csp().noUnsafeEval; 26526 var $parseOptions = { 26527 csp: noUnsafeEval, 26528 literals: copy(literals), 26529 isIdentifierStart: isFunction(identStart) && identStart, 26530 isIdentifierContinue: isFunction(identContinue) && identContinue 26531 }; 26532 return $parse; 26533 26534 function $parse(exp, interceptorFn) { 26535 var parsedExpression, oneTime, cacheKey; 26536 26537 switch (typeof exp) { 26538 case 'string': 26539 exp = exp.trim(); 26540 cacheKey = exp; 26541 26542 parsedExpression = cache[cacheKey]; 26543 26544 if (!parsedExpression) { 26545 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { 26546 oneTime = true; 26547 exp = exp.substring(2); 26548 } 26549 var lexer = new Lexer($parseOptions); 26550 var parser = new Parser(lexer, $filter, $parseOptions); 26551 parsedExpression = parser.parse(exp); 26552 if (parsedExpression.constant) { 26553 parsedExpression.$$watchDelegate = constantWatchDelegate; 26554 } else if (oneTime) { 26555 parsedExpression.$$watchDelegate = parsedExpression.literal ? 26556 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; 26557 } else if (parsedExpression.inputs) { 26558 parsedExpression.$$watchDelegate = inputsWatchDelegate; 26559 } 26560 cache[cacheKey] = parsedExpression; 26561 } 26562 return addInterceptor(parsedExpression, interceptorFn); 26563 26564 case 'function': 26565 return addInterceptor(exp, interceptorFn); 26566 26567 default: 26568 return addInterceptor(noop, interceptorFn); 26569 } 26570 } 26571 26572 function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) { 26573 26574 if (newValue == null || oldValueOfValue == null) { // null/undefined 26575 return newValue === oldValueOfValue; 26576 } 26577 26578 if (typeof newValue === 'object' && !compareObjectIdentity) { 26579 26580 // attempt to convert the value to a primitive type 26581 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can 26582 // be cheaply dirty-checked 26583 newValue = getValueOf(newValue); 26584 26585 if (typeof newValue === 'object') { 26586 // objects/arrays are not supported - deep-watching them would be too expensive 26587 return false;
vendor: 2,350 bytes, lines 26588-26643
26588 } 26589 26590 // fall-through to the primitive equality check 26591 } 26592 26593 //Primitive or NaN 26594 // eslint-disable-next-line no-self-compare 26595 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue); 26596 } 26597 26598 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { 26599 var inputExpressions = parsedExpression.inputs; 26600 var lastResult; 26601 26602 if (inputExpressions.length === 1) { 26603 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails 26604 inputExpressions = inputExpressions[0]; 26605 return scope.$watch(function expressionInputWatch(scope) { 26606 var newInputValue = inputExpressions(scope); 26607 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) { 26608 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]); 26609 oldInputValueOf = newInputValue && getValueOf(newInputValue); 26610 } 26611 return lastResult; 26612 }, listener, objectEquality, prettyPrintExpression); 26613 } 26614 26615 var oldInputValueOfValues = []; 26616 var oldInputValues = []; 26617 for (var i = 0, ii = inputExpressions.length; i < ii; i++) { 26618 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails 26619 oldInputValues[i] = null; 26620 } 26621 26622 return scope.$watch(function expressionInputsWatch(scope) { 26623 var changed = false; 26624 26625 for (var i = 0, ii = inputExpressions.length; i < ii; i++) { 26626 var newInputValue = inputExpressions[i](scope); 26627 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) { 26628 oldInputValues[i] = newInputValue; 26629 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); 26630 } 26631 } 26632 26633 if (changed) { 26634 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues); 26635 } 26636 26637 return lastResult; 26638 }, listener, objectEquality, prettyPrintExpression); 26639 } 26640 26641 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { 26642 var unwatch, lastValue; 26643 if (parsedExpression.inputs) {
26644 unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression); 26645 } else { 26646 unwatch = scope.$watch(oneTimeWatch, oneTimeListener, objectEquality); 26647 } 26648 return unwatch; 26649 26650 function oneTimeWatch(scope) { 26651 return parsedExpression(scope); 26652 } 26653 function oneTimeListener(value, old, scope) { 26654 lastValue = value; 26655 if (isFunction(listener)) { 26656 listener(value, old, scope); 26657 } 26658 if (isDefined(value)) { 26659 scope.$$postDigest(function() { 26660 if (isDefined(lastValue)) { 26661 unwatch(); 26662 } 26663 }); 26664 } 26665 } 26666 } 26667 26668 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { 26669 var unwatch, lastValue; 26670 unwatch = scope.$watch(function oneTimeWatch(scope) { 26671 return parsedExpression(scope); 26672 }, function oneTimeListener(value, old, scope) { 26673 lastValue = value; 26674 if (isFunction(listener)) { 26675 listener(value, old, scope); 26676 } 26677 if (isAllDefined(value)) { 26678 scope.$$postDigest(function() { 26679 if (isAllDefined(lastValue)) unwatch(); 26680 }); 26681 } 26682 }, objectEquality); 26683 26684 return unwatch; 26685 26686 function isAllDefined(value) { 26687 var allDefined = true; 26688 forEach(value, function(val) { 26689 if (!isDefined(val)) allDefined = false; 26690 }); 26691 return allDefined; 26692 } 26693 } 26694 26695 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) { 26696 var unwatch = scope.$watch(function constantWatch(scope) { 26697 unwatch(); 26698 return parsedExpression(scope); 26699 }, listener, objectEquality); 26700 return unwatch; 26701 } 26702 26703 function addInterceptor(parsedExpression, interceptorFn) { 26704 if (!interceptorFn) return parsedExpression; 26705 var watchDelegate = parsedExpression.$$watchDelegate; 26706 var useInputs = false; 26707 26708 var regularWatch = 26709 watchDelegate !== oneTimeLiteralWatchDelegate && 26710 watchDelegate !== oneTimeWatchDelegate; 26711 26712 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) { 26713 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs); 26714 return interceptorFn(value, scope, locals); 26715 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) { 26716 var value = parsedExpression(scope, locals, assign, inputs); 26717 var result = interceptorFn(value, scope, locals); 26718 // we only return the interceptor's result if the 26719 // initial value is defined (for bind-once) 26720 return isDefined(value) ? result : value; 26721 }; 26722 26723 // Propagate $$watchDelegates other then inputsWatchDelegate 26724 useInputs = !parsedExpression.inputs; 26725 if (parsedExpression.$$watchDelegate && 26726 parsedExpression.$$watchDelegate !== inputsWatchDelegate) { 26727 fn.$$watchDelegate = parsedExpression.$$watchDelegate; 26728 fn.inputs = parsedExpression.inputs; 26729 } else if (!interceptorFn.$stateful) { 26730 // If there is an interceptor, but no watchDelegate then treat the interceptor like 26731 // we treat filters - it is assumed to be a pure function unless flagged with $stateful 26732 fn.$$watchDelegate = inputsWatchDelegate; 26733 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]; 26734 } 26735 26736 return fn; 26737 } 26738 }]; 26739} 26740 26741/** 26742 * @ngdoc service 26743 * @name $q 26744 * @requires $rootScope 26745 * 26746 * @description 26747 * A service that helps you run functions asynchronously, and use their return values (or exceptions) 26748 * when they are done processing. 26749 * 26750 * This is a [Promises/A+](https://promisesaplus.com/)-compliant implementation of promises/deferred 26751 * objects inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). 26752 * 26753 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred 26754 * implementations, and the other which resembles ES6 (ES2015) promises to some degree. 26755 * 26756 * # $q constructor 26757 * 26758 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver` 26759 * function as the first argument. This is similar to the native Promise implementation from ES6, 26760 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). 26761 * 26762 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are 26763 * available yet. 26764 * 26765 * It can be used like so: 26766 * 26767 * ```js 26768 * // for the purpose of this example let's assume that variables `$q` and `okToGreet` 26769 * // are available in the current lexical scope (they could have been injected or passed in). 26770 * 26771 * function asyncGreet(name) { 26772 * // perform some asynchronous operation, resolve or reject the promise when appropriate. 26773 * return $q(function(resolve, reject) { 26774 * setTimeout(function() { 26775 * if (okToGreet(name)) { 26776 * resolve('Hello, ' + name + '!'); 26777 * } else { 26778 * reject('Greeting ' + name + ' is not allowed.'); 26779 * } 26780 * }, 1000); 26781 * }); 26782 * } 26783 * 26784 * var promise = asyncGreet('Robin Hood'); 26785 * promise.then(function(greeting) { 26786 * alert('Success: ' + greeting); 26787 * }, function(reason) { 26788 * alert('Failed: ' + reason); 26789 * }); 26790 * ``` 26791 *
26792 * Note: progress/notify callbacks are not currently supported via the ES6-style interface. 26793 * 26794 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise. 26795 * 26796 * However, the more traditional CommonJS-style usage is still available, and documented below. 26797 * 26798 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an 26799 * interface for interacting with an object that represents the result of an action that is 26800 * performed asynchronously, and may or may not be finished at any given point in time. 26801 * 26802 * From the perspective of dealing with error handling, deferred and promise APIs are to 26803 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. 26804 * 26805 * ```js 26806 * // for the purpose of this example let's assume that variables `$q` and `okToGreet` 26807 * // are available in the current lexical scope (they could have been injected or passed in). 26808 * 26809 * function asyncGreet(name) { 26810 * var deferred = $q.defer(); 26811 * 26812 * setTimeout(function() { 26813 * deferred.notify('About to greet ' + name + '.'); 26814 * 26815 * if (okToGreet(name)) { 26816 * deferred.resolve('Hello, ' + name + '!'); 26817 * } else { 26818 * deferred.reject('Greeting ' + name + ' is not allowed.'); 26819 * } 26820 * }, 1000); 26821 * 26822 * return deferred.promise; 26823 * } 26824 * 26825 * var promise = asyncGreet('Robin Hood'); 26826 * promise.then(function(greeting) { 26827 * alert('Success: ' + greeting); 26828 * }, function(reason) { 26829 * alert('Failed: ' + reason); 26830 * }, function(update) { 26831 * alert('Got notification: ' + update); 26832 * }); 26833 * ``` 26834 * 26835 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff 26836 * comes in the way of guarantees that promise and deferred APIs make, see 26837 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. 26838 * 26839 * Additionally the promise api allows for composition that is very hard to do with the 26840 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. 26841 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the 26842 * section on serial or parallel joining of promises. 26843 * 26844 * # The Deferred API 26845 * 26846 * A new instance of deferred is constructed by calling `$q.defer()`. 26847 * 26848 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs 26849 * that can be used for signaling the successful or unsuccessful completion, as well as the status 26850 * of the task. 26851 * 26852 * **Methods** 26853 * 26854 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection 26855 * constructed via `$q.reject`, the promise will be rejected instead. 26856 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to 26857 * resolving it with a rejection constructed via `$q.reject`. 26858 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called 26859 * multiple times before the promise is either resolved or rejected. 26860 * 26861 * **Properties** 26862 * 26863 * - promise – `{Promise}` – promise object associated with this deferred. 26864 * 26865 * 26866 * # The Promise API 26867 * 26868 * A new promise instance is created when a deferred instance is created and can be retrieved by 26869 * calling `deferred.promise`. 26870 * 26871 * The purpose of the promise object is to allow for interested parties to get access to the result 26872 * of the deferred task when it completes. 26873 * 26874 * **Methods** 26875 * 26876 * - `then(successCallback, [errorCallback], [notifyCallback])` – regardless of when the promise was or 26877 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously 26878 * as soon as the result is available. The callbacks are called with a single argument: the result 26879 * or rejection reason. Additionally, the notify callback may be called zero or more times to 26880 * provide a progress indication, before the promise is resolved or rejected. 26881 * 26882 * This method *returns a new promise* which is resolved or rejected via the return value of the 26883 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
26884 * with the value which is resolved in that promise using 26885 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)). 26886 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be 26887 * resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback 26888 * arguments are optional. 26889 * 26890 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` 26891 * 26892 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise, 26893 * but to do so without modifying the final value. This is useful to release resources or do some 26894 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full 26895 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for 26896 * more information. 26897 * 26898 * # Chaining promises 26899 * 26900 * Because calling the `then` method of a promise returns a new derived promise, it is easily 26901 * possible to create a chain of promises: 26902 * 26903 * ```js 26904 * promiseB = promiseA.then(function(result) { 26905 * return result + 1; 26906 * }); 26907 * 26908 * // promiseB will be resolved immediately after promiseA is resolved and its value 26909 * // will be the result of promiseA incremented by 1 26910 * ``` 26911 * 26912 * It is possible to create chains of any length and since a promise can be resolved with another 26913 * promise (which will defer its resolution further), it is possible to pause/defer resolution of 26914 * the promises at any point in the chain. This makes it possible to implement powerful APIs like 26915 * $http's response interceptors. 26916 * 26917 * 26918 * # Differences between Kris Kowal's Q and $q 26919 * 26920 * There are two main differences: 26921 * 26922 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation 26923 * mechanism in angular, which means faster propagation of resolution or rejection into your 26924 * models and avoiding unnecessary browser repaints, which would result in flickering UI. 26925 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
26926 * all the important functionality needed for common async tasks. 26927 * 26928 * # Testing 26929 * 26930 * ```js 26931 * it('should simulate promise', inject(function($q, $rootScope) { 26932 * var deferred = $q.defer(); 26933 * var promise = deferred.promise; 26934 * var resolvedValue; 26935 * 26936 * promise.then(function(value) { resolvedValue = value; }); 26937 * expect(resolvedValue).toBeUndefined(); 26938 * 26939 * // Simulate resolving of promise 26940 * deferred.resolve(123); 26941 * // Note that the 'then' function does not get called synchronously. 26942 * // This is because we want the promise API to always be async, whether or not 26943 * // it got called synchronously or asynchronously. 26944 * expect(resolvedValue).toBeUndefined(); 26945 * 26946 * // Propagate promise resolution to 'then' functions using $apply(). 26947 * $rootScope.$apply(); 26948 * expect(resolvedValue).toEqual(123); 26949 * })); 26950 * ``` 26951 * 26952 * @param {function(function, function)} resolver Function which is responsible for resolving or 26953 * rejecting the newly created promise. The first parameter is a function which resolves the 26954 * promise, the second parameter is a function which rejects the promise. 26955 * 26956 * @returns {Promise} The newly created promise. 26957 */ 26958/** 26959 * @ngdoc provider 26960 * @name $qProvider 26961 * @this 26962 * 26963 * @description 26964 */ 26965function $QProvider() { 26966 var errorOnUnhandledRejections = true; 26967 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { 26968 return qFactory(function(callback) { 26969 $rootScope.$evalAsync(callback); 26970 }, $exceptionHandler, errorOnUnhandledRejections); 26971 }]; 26972 26973 /** 26974 * @ngdoc method 26975 * @name $qProvider#errorOnUnhandledRejections 26976 * @kind function 26977 * 26978 * @description 26979 * Retrieves or overrides whether to generate an error when a rejected promise is not handled. 26980 * This feature is enabled by default. 26981 * 26982 * @param {boolean=} value Whether to generate an error when a rejected promise is not handled. 26983 * @returns {boolean|ng.$qProvider} Current value when called without a new value or self for 26984 * chaining otherwise. 26985 */ 26986 this.errorOnUnhandledRejections = function(value) { 26987 if (isDefined(value)) { 26988 errorOnUnhandledRejections = value; 26989 return this; 26990 } else { 26991 return errorOnUnhandledRejections; 26992 } 26993 }; 26994} 26995 26996/** @this */ 26997function $$QProvider() { 26998 var errorOnUnhandledRejections = true; 26999 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) { 27000 return qFactory(function(callback) { 27001 $browser.defer(callback); 27002 }, $exceptionHandler, errorOnUnhandledRejections); 27003 }]; 27004 27005 this.errorOnUnhandledRejections = function(value) { 27006 if (isDefined(value)) { 27007 errorOnUnhandledRejections = value; 27008 return this; 27009 } else { 27010 return errorOnUnhandledRejections; 27011 } 27012 }; 27013} 27014 27015/** 27016 * Constructs a promise manager. 27017 * 27018 * @param {function(function)} nextTick Function for executing functions in the next turn. 27019 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for 27020 * debugging purposes. 27021 @ param {=boolean} errorOnUnhandledRejections Whether an error should be generated on unhandled 27022 * promises rejections. 27023 * @returns {object} Promise manager. 27024 */ 27025function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) { 27026 var $qMinErr = minErr('$q', TypeError); 27027 var queueSize = 0; 27028 var checkQueue = []; 27029 27030 /** 27031 * @ngdoc method 27032 * @name ng.$q#defer 27033 * @kind function 27034 * 27035 * @description 27036 * Creates a `Deferred` object which represents a task which will finish in the future. 27037 * 27038 * @returns {Deferred} Returns a new instance of deferred. 27039 */ 27040 function defer() { 27041 return new Deferred(); 27042 } 27043 27044 function Deferred() { 27045 var promise = this.promise = new Promise(); 27046 //Non prototype methods necessary to support unbound execution :/ 27047 this.resolve = function(val) { resolvePromise(promise, val); }; 27048 this.reject = function(reason) { rejectPromise(promise, reason); }; 27049 this.notify = function(progress) { notifyPromise(promise, progress); }; 27050 } 27051 27052 27053 function Promise() { 27054 this.$$state = { status: 0 }; 27055 } 27056 27057 extend(Promise.prototype, { 27058 then: function(onFulfilled, onRejected, progressBack) { 27059 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) { 27060 return this; 27061 } 27062 var result = new Promise(); 27063 27064 this.$$state.pending = this.$$state.pending || []; 27065 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
vendor: 5,152 bytes, lines 27066-27241
27066 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state); 27067 27068 return result; 27069 }, 27070 27071 'catch': function(callback) { 27072 return this.then(null, callback); 27073 }, 27074 27075 'finally': function(callback, progressBack) { 27076 return this.then(function(value) { 27077 return handleCallback(value, resolve, callback); 27078 }, function(error) { 27079 return handleCallback(error, reject, callback); 27080 }, progressBack); 27081 } 27082 }); 27083 27084 function processQueue(state) { 27085 var fn, promise, pending; 27086 27087 pending = state.pending; 27088 state.processScheduled = false; 27089 state.pending = undefined; 27090 try { 27091 for (var i = 0, ii = pending.length; i < ii; ++i) { 27092 state.pur = true; 27093 promise = pending[i][0]; 27094 fn = pending[i][state.status]; 27095 try { 27096 if (isFunction(fn)) { 27097 resolvePromise(promise, fn(state.value)); 27098 } else if (state.status === 1) { 27099 resolvePromise(promise, state.value); 27100 } else { 27101 rejectPromise(promise, state.value); 27102 } 27103 } catch (e) { 27104 rejectPromise(promise, e); 27105 } 27106 } 27107 } finally { 27108 --queueSize; 27109 if (errorOnUnhandledRejections && queueSize === 0) { 27110 nextTick(processChecks); 27111 } 27112 } 27113 } 27114 27115 function processChecks() { 27116 // eslint-disable-next-line no-unmodified-loop-condition 27117 while (!queueSize && checkQueue.length) { 27118 var toCheck = checkQueue.shift(); 27119 if (!toCheck.pur) { 27120 toCheck.pur = true; 27121 var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value); 27122 if (toCheck.value instanceof Error) { 27123 exceptionHandler(toCheck.value, errorMessage); 27124 } else { 27125 exceptionHandler(errorMessage); 27126 } 27127 } 27128 } 27129 } 27130 27131 function scheduleProcessQueue(state) { 27132 if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !state.pur) { 27133 if (queueSize === 0 && checkQueue.length === 0) { 27134 nextTick(processChecks); 27135 } 27136 checkQueue.push(state); 27137 } 27138 if (state.processScheduled || !state.pending) return; 27139 state.processScheduled = true; 27140 ++queueSize; 27141 nextTick(function() { processQueue(state); }); 27142 } 27143 27144 function resolvePromise(promise, val) { 27145 if (promise.$$state.status) return; 27146 if (val === promise) { 27147 $$reject(promise, $qMinErr( 27148 'qcycle', 27149 'Expected promise to be resolved with value other than itself \'{0}\'', 27150 val)); 27151 } else { 27152 $$resolve(promise, val); 27153 } 27154 27155 } 27156 27157 function $$resolve(promise, val) { 27158 var then; 27159 var done = false; 27160 try { 27161 if (isObject(val) || isFunction(val)) then = val.then; 27162 if (isFunction(then)) { 27163 promise.$$state.status = -1; 27164 then.call(val, doResolve, doReject, doNotify); 27165 } else { 27166 promise.$$state.value = val; 27167 promise.$$state.status = 1; 27168 scheduleProcessQueue(promise.$$state); 27169 } 27170 } catch (e) { 27171 doReject(e); 27172 } 27173 27174 function doResolve(val) { 27175 if (done) return; 27176 done = true; 27177 $$resolve(promise, val); 27178 } 27179 function doReject(val) { 27180 if (done) return; 27181 done = true; 27182 $$reject(promise, val); 27183 } 27184 function doNotify(progress) { 27185 notifyPromise(promise, progress); 27186 } 27187 } 27188 27189 function rejectPromise(promise, reason) { 27190 if (promise.$$state.status) return; 27191 $$reject(promise, reason); 27192 } 27193 27194 function $$reject(promise, reason) { 27195 promise.$$state.value = reason; 27196 promise.$$state.status = 2; 27197 scheduleProcessQueue(promise.$$state); 27198 } 27199 27200 function notifyPromise(promise, progress) { 27201 var callbacks = promise.$$state.pending; 27202 27203 if ((promise.$$state.status <= 0) && callbacks && callbacks.length) { 27204 nextTick(function() { 27205 var callback, result; 27206 for (var i = 0, ii = callbacks.length; i < ii; i++) { 27207 result = callbacks[i][0]; 27208 callback = callbacks[i][3]; 27209 try { 27210 notifyPromise(result, isFunction(callback) ? callback(progress) : progress); 27211 } catch (e) { 27212 exceptionHandler(e); 27213 } 27214 } 27215 }); 27216 } 27217 } 27218 27219 /** 27220 * @ngdoc method 27221 * @name $q#reject 27222 * @kind function 27223 * 27224 * @description 27225 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be 27226 * used to forward rejection in a chain of promises. If you are dealing with the last promise in 27227 * a promise chain, you don't need to worry about it. 27228 * 27229 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of 27230 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via 27231 * a promise error callback and you want to forward the error to the promise derived from the 27232 * current promise, you have to "rethrow" the error by returning a rejection constructed via 27233 * `reject`. 27234 * 27235 * ```js 27236 * promiseB = promiseA.then(function(result) { 27237 * // success: do something and resolve promiseB 27238 * // with the old or a new result 27239 * return result; 27240 * }, function(reason) { 27241 * // error: handle the error if possible and
27242 * // resolve promiseB with newPromiseOrValue, 27243 * // otherwise forward the rejection to promiseB 27244 * if (canHandle(reason)) { 27245 * // handle the error and recover 27246 * return newPromiseOrValue; 27247 * } 27248 * return $q.reject(reason); 27249 * }); 27250 * ``` 27251 * 27252 * @param {*} reason Constant, message, exception or an object representing the rejection reason. 27253 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. 27254 */ 27255 function reject(reason) { 27256 var result = new Promise(); 27257 rejectPromise(result, reason); 27258 return result; 27259 } 27260 27261 function handleCallback(value, resolver, callback) { 27262 var callbackOutput = null; 27263 try { 27264 if (isFunction(callback)) callbackOutput = callback(); 27265 } catch (e) { 27266 return reject(e); 27267 } 27268 if (isPromiseLike(callbackOutput)) { 27269 return callbackOutput.then(function() { 27270 return resolver(value); 27271 }, reject); 27272 } else { 27273 return resolver(value); 27274 } 27275 } 27276 27277 /** 27278 * @ngdoc method 27279 * @name $q#when 27280 * @kind function 27281 * 27282 * @description 27283 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. 27284 * This is useful when you are dealing with an object that might or might not be a promise, or if 27285 * the promise comes from a source that can't be trusted. 27286 * 27287 * @param {*} value Value or a promise 27288 * @param {Function=} successCallback 27289 * @param {Function=} errorCallback 27290 * @param {Function=} progressCallback 27291 * @returns {Promise} Returns a promise of the passed value or promise 27292 */ 27293 27294 27295 function when(value, callback, errback, progressBack) { 27296 var result = new Promise(); 27297 resolvePromise(result, value); 27298 return result.then(callback, errback, progressBack); 27299 } 27300 27301 /** 27302 * @ngdoc method 27303 * @name $q#resolve 27304 * @kind function 27305 * 27306 * @description 27307 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6. 27308 * 27309 * @param {*} value Value or a promise 27310 * @param {Function=} successCallback 27311 * @param {Function=} errorCallback 27312 * @param {Function=} progressCallback 27313 * @returns {Promise} Returns a promise of the passed value or promise 27314 */ 27315 var resolve = when; 27316 27317 /** 27318 * @ngdoc method 27319 * @name $q#all 27320 * @kind function 27321 * 27322 * @description 27323 * Combines multiple promises into a single promise that is resolved when all of the input 27324 * promises are resolved. 27325 * 27326 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises. 27327 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, 27328 * each value corresponding to the promise at the same index/key in the `promises` array/hash. 27329 * If any of the promises is resolved with a rejection, this resulting promise will be rejected 27330 * with the same rejection value. 27331 */ 27332 27333 function all(promises) { 27334 var result = new Promise(), 27335 counter = 0, 27336 results = isArray(promises) ? [] : {}; 27337 27338 forEach(promises, function(promise, key) { 27339 counter++; 27340 when(promise).then(function(value) { 27341 results[key] = value; 27342 if (!(--counter)) resolvePromise(result, results); 27343 }, function(reason) { 27344 rejectPromise(result, reason); 27345 }); 27346 }); 27347 27348 if (counter === 0) { 27349 resolvePromise(result, results); 27350 } 27351 27352 return result; 27353 } 27354 27355 /** 27356 * @ngdoc method 27357 * @name $q#race 27358 * @kind function 27359 * 27360 * @description 27361 * Returns a promise that resolves or rejects as soon as one of those promises 27362 * resolves or rejects, with the value or reason from that promise. 27363 * 27364 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises. 27365 * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises` 27366 * resolves or rejects, with the value or reason from that promise. 27367 */ 27368 27369 function race(promises) { 27370 var deferred = defer(); 27371 27372 forEach(promises, function(promise) { 27373 when(promise).then(deferred.resolve, deferred.reject); 27374 }); 27375 27376 return deferred.promise; 27377 } 27378 27379 function $Q(resolver) { 27380 if (!isFunction(resolver)) { 27381 throw $qMinErr('norslvr', 'Expected resolverFn, got \'{0}\'', resolver); 27382 } 27383 27384 var promise = new Promise(); 27385 27386 function resolveFn(value) { 27387 resolvePromise(promise, value); 27388 } 27389 27390 function rejectFn(reason) { 27391 rejectPromise(promise, reason); 27392 } 27393 27394 resolver(resolveFn, rejectFn); 27395 27396 return promise; 27397 } 27398 27399 // Let's make the instanceof operator work for promises, so that 27400 // `new $q(fn) instanceof $q` would evaluate to true. 27401 $Q.prototype = Promise.prototype; 27402 27403 $Q.defer = defer; 27404 $Q.reject = reject; 27405 $Q.when = when; 27406 $Q.resolve = resolve; 27407 $Q.all = all; 27408 $Q.race = race; 27409 27410 return $Q; 27411} 27412 27413/** @this */ 27414function $$RAFProvider() { //rAF 27415 this.$get = ['$window', '$timeout', function($window, $timeout) { 27416 var requestAnimationFrame = $window.requestAnimationFrame || 27417 $window.webkitRequestAnimationFrame; 27418 27419 var cancelAnimationFrame = $window.cancelAnimationFrame || 27420 $window.webkitCancelAnimationFrame || 27421 $window.webkitCancelRequestAnimationFrame; 27422 27423 var rafSupported = !!requestAnimationFrame; 27424 var raf = rafSupported 27425 ? function(fn) { 27426 var id = requestAnimationFrame(fn); 27427 return function() { 27428 cancelAnimationFrame(id); 27429 }; 27430 } 27431 : function(fn) { 27432 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 27433 return function() { 27434 $timeout.cancel(timer); 27435 }; 27436 }; 27437 27438 raf.supported = rafSupported; 27439 27440 return raf; 27441 }]; 27442} 27443 27444/** 27445 * DESIGN NOTES 27446 * 27447 * The design decisions behind the scope are heavily favored for speed and memory consumption. 27448 * 27449 * The typical use of scope is to watch the expressions, which most of the time return the same 27450 * value as last time so we optimize the operation. 27451 * 27452 * Closures construction is expensive in terms of speed as well as memory: 27453 * - No closures, instead use prototypical inheritance for API 27454 * - Internal state needs to be stored on scope directly, which means that private state is 27455 * exposed as $$____ properties 27456 * 27457 * Loop operations are optimized by using while(count--) { ... } 27458 * - This means that in order to keep the same order of execution as addition we have to add 27459 * items to the array at the beginning (unshift) instead of at the end (push) 27460 * 27461 * Child scopes are created and removed often 27462 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists 27463 * 27464 * There are fewer watches than observers. This is why you don't want the observer to be implemented 27465 * in the same way as watch. Watch requires return of the initialization function which is expensive 27466 * to construct. 27467 */ 27468 27469 27470/** 27471 * @ngdoc provider 27472 * @name $rootScopeProvider 27473 * @description 27474 * 27475 * Provider for the $rootScope service. 27476 */ 27477 27478/** 27479 * @ngdoc method 27480 * @name $rootScopeProvider#digestTtl 27481 * @description 27482 * 27483 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and 27484 * assuming that the model is unstable. 27485 * 27486 * The current default is 10 iterations. 27487 * 27488 * In complex applications it's possible that the dependencies between `$watch`s will result in 27489 * several digest iterations. However if an application needs more than the default 10 digest 27490 * iterations for its model to stabilize then you should investigate what is causing the model to 27491 * continuously change during the digest. 27492 * 27493 * Increasing the TTL could have performance implications, so you should not change it without 27494 * proper justification. 27495 * 27496 * @param {number} limit The number of digest iterations. 27497 */ 27498 27499 27500/** 27501 * @ngdoc service 27502 * @name $rootScope 27503 * @this 27504 * 27505 * @description 27506 * 27507 * Every application has a single root {@link ng.$rootScope.Scope scope}. 27508 * All other scopes are descendant scopes of the root scope. Scopes provide separation 27509 * between the model and the view, via a mechanism for watching the model for changes. 27510 * They also provide event emission/broadcast and subscription facility. See the 27511 * {@link guide/scope developer guide on scopes}. 27512 */ 27513function $RootScopeProvider() { 27514 var TTL = 10; 27515 var $rootScopeMinErr = minErr('$rootScope'); 27516 var lastDirtyWatch = null; 27517 var applyAsyncId = null; 27518 27519 this.digestTtl = function(value) { 27520 if (arguments.length) { 27521 TTL = value; 27522 } 27523 return TTL; 27524 }; 27525 27526 function createChildScopeClass(parent) { 27527 function ChildScope() {
vendor: 11,072 bytes, lines 27528-27788
27528 this.$$watchers = this.$$nextSibling = 27529 this.$$childHead = this.$$childTail = null; 27530 this.$$listeners = {}; 27531 this.$$listenerCount = {}; 27532 this.$$watchersCount = 0; 27533 this.$id = nextUid(); 27534 this.$$ChildScope = null; 27535 } 27536 ChildScope.prototype = parent; 27537 return ChildScope; 27538 } 27539 27540 this.$get = ['$exceptionHandler', '$parse', '$browser', 27541 function($exceptionHandler, $parse, $browser) { 27542 27543 function destroyChildScope($event) { 27544 $event.currentScope.$$destroyed = true; 27545 } 27546 27547 function cleanUpScope($scope) { 27548 27549 // Support: IE 9 only 27550 if (msie === 9) { 27551 // There is a memory leak in IE9 if all child scopes are not disconnected 27552 // completely when a scope is destroyed. So this code will recurse up through 27553 // all this scopes children 27554 // 27555 // See issue https://github.com/angular/angular.js/issues/10706 27556 if ($scope.$$childHead) { 27557 cleanUpScope($scope.$$childHead); 27558 } 27559 if ($scope.$$nextSibling) { 27560 cleanUpScope($scope.$$nextSibling); 27561 } 27562 } 27563 27564 // The code below works around IE9 and V8's memory leaks 27565 // 27566 // See: 27567 // - https://code.google.com/p/v8/issues/detail?id=2073#c26 27568 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 27569 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 27570 27571 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead = 27572 $scope.$$childTail = $scope.$root = $scope.$$watchers = null; 27573 } 27574 27575 /** 27576 * @ngdoc type 27577 * @name $rootScope.Scope 27578 * 27579 * @description 27580 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the 27581 * {@link auto.$injector $injector}. Child scopes are created using the 27582 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when 27583 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for 27584 * an in-depth introduction and usage examples. 27585 * 27586 * 27587 * # Inheritance 27588 * A scope can inherit from a parent scope, as in this example: 27589 * ```js 27590 var parent = $rootScope; 27591 var child = parent.$new(); 27592 27593 parent.salutation = "Hello"; 27594 expect(child.salutation).toEqual('Hello'); 27595 27596 child.salutation = "Welcome"; 27597 expect(child.salutation).toEqual('Welcome'); 27598 expect(parent.salutation).toEqual('Hello'); 27599 * ``` 27600 * 27601 * When interacting with `Scope` in tests, additional helper methods are available on the 27602 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional 27603 * details. 27604 * 27605 * 27606 * @param {Object.<string, function()>=} providers Map of service factory which need to be 27607 * provided for the current scope. Defaults to {@link ng}. 27608 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should 27609 * append/override services provided by `providers`. This is handy 27610 * when unit-testing and having the need to override a default 27611 * service. 27612 * @returns {Object} Newly created scope. 27613 * 27614 */ 27615 function Scope() { 27616 this.$id = nextUid(); 27617 this.$$phase = this.$parent = this.$$watchers = 27618 this.$$nextSibling = this.$$prevSibling = 27619 this.$$childHead = this.$$childTail = null; 27620 this.$root = this; 27621 this.$$destroyed = false; 27622 this.$$listeners = {}; 27623 this.$$listenerCount = {}; 27624 this.$$watchersCount = 0; 27625 this.$$isolateBindings = null; 27626 } 27627 27628 /** 27629 * @ngdoc property 27630 * @name $rootScope.Scope#$id 27631 * 27632 * @description 27633 * Unique scope ID (monotonically increasing) useful for debugging. 27634 */ 27635 27636 /** 27637 * @ngdoc property 27638 * @name $rootScope.Scope#$parent 27639 * 27640 * @description 27641 * Reference to the parent scope. 27642 */ 27643 27644 /** 27645 * @ngdoc property 27646 * @name $rootScope.Scope#$root 27647 * 27648 * @description 27649 * Reference to the root scope. 27650 */ 27651 27652 Scope.prototype = { 27653 constructor: Scope, 27654 /** 27655 * @ngdoc method 27656 * @name $rootScope.Scope#$new 27657 * @kind function 27658 * 27659 * @description 27660 * Creates a new child {@link ng.$rootScope.Scope scope}. 27661 * 27662 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. 27663 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. 27664 * 27665 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is 27666 * desired for the scope and its child scopes to be permanently detached from the parent and 27667 * thus stop participating in model change detection and listener notification by invoking. 27668 * 27669 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the 27670 * parent scope. The scope is isolated, as it can not see parent scope properties. 27671 * When creating widgets, it is useful for the widget to not accidentally read parent 27672 * state. 27673 * 27674 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent` 27675 * of the newly created scope. Defaults to `this` scope if not provided. 27676 * This is used when creating a transclude scope to correctly place it 27677 * in the scope hierarchy while maintaining the correct prototypical 27678 * inheritance. 27679 * 27680 * @returns {Object} The newly created child scope. 27681 * 27682 */ 27683 $new: function(isolate, parent) { 27684 var child; 27685 27686 parent = parent || this; 27687 27688 if (isolate) { 27689 child = new Scope(); 27690 child.$root = this.$root; 27691 } else { 27692 // Only create a child scope class if somebody asks for one, 27693 // but cache it to allow the VM to optimize lookups. 27694 if (!this.$$ChildScope) { 27695 this.$$ChildScope = createChildScopeClass(this); 27696 } 27697 child = new this.$$ChildScope(); 27698 } 27699 child.$parent = parent; 27700 child.$$prevSibling = parent.$$childTail; 27701 if (parent.$$childHead) { 27702 parent.$$childTail.$$nextSibling = child; 27703 parent.$$childTail = child; 27704 } else { 27705 parent.$$childHead = parent.$$childTail = child; 27706 } 27707 27708 // When the new scope is not isolated or we inherit from `this`, and 27709 // the parent scope is destroyed, the property `$$destroyed` is inherited 27710 // prototypically. In all other cases, this property needs to be set 27711 // when the parent scope is destroyed. 27712 // The listener needs to be added after the parent is set 27713 if (isolate || parent !== this) child.$on('$destroy', destroyChildScope); 27714 27715 return child; 27716 }, 27717 27718 /** 27719 * @ngdoc method 27720 * @name $rootScope.Scope#$watch 27721 * @kind function 27722 * 27723 * @description 27724 * Registers a `listener` callback to be executed whenever the `watchExpression` changes. 27725 * 27726 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest 27727 * $digest()} and should return the value that will be watched. (`watchExpression` should not change 27728 * its value when executed multiple times with the same input because it may be executed multiple 27729 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be 27730 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).) 27731 * - The `listener` is called only when the value from the current `watchExpression` and the 27732 * previous call to `watchExpression` are not equal (with the exception of the initial run, 27733 * see below). Inequality is determined according to reference inequality, 27734 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) 27735 * via the `!==` Javascript operator, unless `objectEquality == true` 27736 * (see next point) 27737 * - When `objectEquality == true`, inequality of the `watchExpression` is determined 27738 * according to the {@link angular.equals} function. To save the value of the object for 27739 * later comparison, the {@link angular.copy} function is used. This therefore means that 27740 * watching complex objects will have adverse memory and performance implications. 27741 * - This should not be used to watch for changes in objects that are 27742 * or contain [File](https://developer.mozilla.org/docs/Web/API/File) objects due to limitations with {@link angular.copy `angular.copy`}. 27743 * - The watch `listener` may change the model, which may trigger other `listener`s to fire. 27744 * This is achieved by rerunning the watchers until no changes are detected. The rerun 27745 * iteration limit is 10 to prevent an infinite loop deadlock. 27746 * 27747 * 27748 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, 27749 * you can register a `watchExpression` function with no `listener`. (Be prepared for 27750 * multiple calls to your `watchExpression` because it will execute multiple times in a 27751 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.) 27752 * 27753 * After a watcher is registered with the scope, the `listener` fn is called asynchronously 27754 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the 27755 * watcher. In rare cases, this is undesirable because the listener is called when the result 27756 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you 27757 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the 27758 * listener was called due to initialization. 27759 * 27760 * 27761 * 27762 * # Example 27763 * ```js 27764 // let's assume that scope was dependency injected as the $rootScope 27765 var scope = $rootScope; 27766 scope.name = 'misko'; 27767 scope.counter = 0; 27768 27769 expect(scope.counter).toEqual(0); 27770 scope.$watch('name', function(newValue, oldValue) { 27771 scope.counter = scope.counter + 1; 27772 }); 27773 expect(scope.counter).toEqual(0); 27774 27775 scope.$digest(); 27776 // the listener is always called during the first $digest loop after it was registered 27777 expect(scope.counter).toEqual(1); 27778 27779 scope.$digest(); 27780 // but now it will not be called unless the value changes 27781 expect(scope.counter).toEqual(1); 27782 27783 scope.name = 'adam'; 27784 scope.$digest(); 27785 expect(scope.counter).toEqual(2); 27786 27787 27788
27789 // Using a function as a watchExpression 27790 var food; 27791 scope.foodCounter = 0; 27792 expect(scope.foodCounter).toEqual(0); 27793 scope.$watch( 27794 // This function returns the value being watched. It is called for each turn of the $digest loop 27795 function() { return food; }, 27796 // This is the change listener, called when the value returned from the above function changes 27797 function(newValue, oldValue) { 27798 if ( newValue !== oldValue ) { 27799 // Only increment the counter if the value changed 27800 scope.foodCounter = scope.foodCounter + 1; 27801 } 27802 } 27803 ); 27804 // No digest has been run so the counter will be zero 27805 expect(scope.foodCounter).toEqual(0); 27806 27807 // Run the digest but since food has not changed count will still be zero 27808 scope.$digest(); 27809 expect(scope.foodCounter).toEqual(0); 27810 27811 // Update food and run digest. Now the counter will increment 27812 food = 'cheeseburger'; 27813 scope.$digest(); 27814 expect(scope.foodCounter).toEqual(1); 27815 27816 * ``` 27817 * 27818 * 27819 * 27820 * @param {(function()|string)} watchExpression Expression that is evaluated on each 27821 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers 27822 * a call to the `listener`. 27823 * 27824 * - `string`: Evaluated as {@link guide/expression expression} 27825 * - `function(scope)`: called with current `scope` as a parameter. 27826 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value 27827 * of `watchExpression` changes. 27828 * 27829 * - `newVal` contains the current value of the `watchExpression` 27830 * - `oldVal` contains the previous value of the `watchExpression` 27831 * - `scope` refers to the current scope 27832 * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of 27833 * comparing for reference equality. 27834 * @returns {function()} Returns a deregistration function for this listener. 27835 */ 27836 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) { 27837 var get = $parse(watchExp); 27838 27839 if (get.$$watchDelegate) { 27840 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp); 27841 } 27842 var scope = this, 27843 array = scope.$$watchers, 27844 watcher = { 27845 fn: listener, 27846 last: initWatchVal, 27847 get: get, 27848 exp: prettyPrintExpression || watchExp, 27849 eq: !!objectEquality 27850 }; 27851 27852 lastDirtyWatch = null; 27853 27854 if (!isFunction(listener)) { 27855 watcher.fn = noop; 27856 } 27857 27858 if (!array) { 27859 array = scope.$$watchers = []; 27860 array.$$digestWatchIndex = -1; 27861 } 27862 // we use unshift since we use a while loop in $digest for speed. 27863 // the while loop reads in reverse order. 27864 array.unshift(watcher); 27865 array.$$digestWatchIndex++; 27866 incrementWatchersCount(this, 1); 27867 27868 return function deregisterWatch() { 27869 var index = arrayRemove(array, watcher); 27870 if (index >= 0) { 27871 incrementWatchersCount(scope, -1); 27872 if (index < array.$$digestWatchIndex) { 27873 array.$$digestWatchIndex--; 27874 } 27875 } 27876 lastDirtyWatch = null; 27877 }; 27878 }, 27879 27880 /** 27881 * @ngdoc method 27882 * @name $rootScope.Scope#$watchGroup 27883 * @kind function 27884 * 27885 * @description 27886 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`. 27887 * If any one expression in the collection changes the `listener` is executed. 27888 * 27889 * - The items in the `watchExpressions` array are observed via the standard `$watch` operation. Their return 27890 * values are examined for changes on every call to `$digest`. 27891 * - The `listener` is called whenever any expression in the `watchExpressions` array changes. 27892 * 27893 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually 27894 * watched using {@link ng.$rootScope.Scope#$watch $watch()} 27895 * 27896 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any 27897 * expression in `watchExpressions` changes 27898 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching 27899 * those of `watchExpression` 27900 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching 27901 * those of `watchExpression` 27902 * The `scope` refers to the current scope. 27903 * @returns {function()} Returns a de-registration function for all listeners. 27904 */ 27905 $watchGroup: function(watchExpressions, listener) { 27906 var oldValues = new Array(watchExpressions.length); 27907 var newValues = new Array(watchExpressions.length); 27908 var deregisterFns = []; 27909 var self = this; 27910 var changeReactionScheduled = false;
vendor: 4,272 bytes, lines 27911-28020
27911 var firstRun = true; 27912 27913 if (!watchExpressions.length) { 27914 // No expressions means we call the listener ASAP 27915 var shouldCall = true; 27916 self.$evalAsync(function() { 27917 if (shouldCall) listener(newValues, newValues, self); 27918 }); 27919 return function deregisterWatchGroup() { 27920 shouldCall = false; 27921 }; 27922 } 27923 27924 if (watchExpressions.length === 1) { 27925 // Special case size of one 27926 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) { 27927 newValues[0] = value; 27928 oldValues[0] = oldValue; 27929 listener(newValues, (value === oldValue) ? newValues : oldValues, scope); 27930 }); 27931 } 27932 27933 forEach(watchExpressions, function(expr, i) { 27934 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { 27935 newValues[i] = value; 27936 oldValues[i] = oldValue; 27937 if (!changeReactionScheduled) { 27938 changeReactionScheduled = true; 27939 self.$evalAsync(watchGroupAction); 27940 } 27941 }); 27942 deregisterFns.push(unwatchFn); 27943 }); 27944 27945 function watchGroupAction() { 27946 changeReactionScheduled = false; 27947 27948 if (firstRun) { 27949 firstRun = false; 27950 listener(newValues, newValues, self); 27951 } else { 27952 listener(newValues, oldValues, self); 27953 } 27954 } 27955 27956 return function deregisterWatchGroup() { 27957 while (deregisterFns.length) { 27958 deregisterFns.shift()(); 27959 } 27960 }; 27961 }, 27962 27963 27964 /** 27965 * @ngdoc method 27966 * @name $rootScope.Scope#$watchCollection 27967 * @kind function 27968 * 27969 * @description 27970 * Shallow watches the properties of an object and fires whenever any of the properties change 27971 * (for arrays, this implies watching the array items; for object maps, this implies watching 27972 * the properties). If a change is detected, the `listener` callback is fired. 27973 * 27974 * - The `obj` collection is observed via standard $watch operation and is examined on every 27975 * call to $digest() to see if any items have been added, removed, or moved. 27976 * - The `listener` is called whenever anything within the `obj` has changed. Examples include 27977 * adding, removing, and moving items belonging to an object or array. 27978 * 27979 * 27980 * # Example 27981 * ```js 27982 $scope.names = ['igor', 'matias', 'misko', 'james']; 27983 $scope.dataCount = 4; 27984 27985 $scope.$watchCollection('names', function(newNames, oldNames) { 27986 $scope.dataCount = newNames.length; 27987 }); 27988 27989 expect($scope.dataCount).toEqual(4); 27990 $scope.$digest(); 27991 27992 //still at 4 ... no changes 27993 expect($scope.dataCount).toEqual(4); 27994 27995 $scope.names.pop(); 27996 $scope.$digest(); 27997 27998 //now there's been a change 27999 expect($scope.dataCount).toEqual(3); 28000 * ``` 28001 * 28002 * 28003 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The 28004 * expression value should evaluate to an object or an array which is observed on each 28005 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the 28006 * collection will trigger a call to the `listener`. 28007 * 28008 * @param {function(newCollection, oldCollection, scope)} listener a callback function called 28009 * when a change is detected. 28010 * - The `newCollection` object is the newly modified data obtained from the `obj` expression 28011 * - The `oldCollection` object is a copy of the former collection data. 28012 * Due to performance considerations, the`oldCollection` value is computed only if the 28013 * `listener` function declares two or more arguments. 28014 * - The `scope` argument refers to the current scope. 28015 * 28016 * @returns {function()} Returns a de-registration function for this listener. When the 28017 * de-registration function is executed, the internal watch operation is terminated. 28018 */ 28019 $watchCollection: function(obj, listener) { 28020 $watchCollectionInterceptor.$stateful = tru
vendor: 6,149 bytes, lines 28020-28178
28020e; 28021 28022 var self = this; 28023 // the current value, updated on each dirty-check run 28024 var newValue; 28025 // a shallow copy of the newValue from the last dirty-check run, 28026 // updated to match newValue during dirty-check run 28027 var oldValue; 28028 // a shallow copy of the newValue from when the last change happened 28029 var veryOldValue; 28030 // only track veryOldValue if the listener is asking for it 28031 var trackVeryOldValue = (listener.length > 1); 28032 var changeDetected = 0; 28033 var changeDetector = $parse(obj, $watchCollectionInterceptor); 28034 var internalArray = []; 28035 var internalObject = {}; 28036 var initRun = true; 28037 var oldLength = 0; 28038 28039 function $watchCollectionInterceptor(_value) { 28040 newValue = _value; 28041 var newLength, key, bothNaN, newItem, oldItem; 28042 28043 // If the new value is undefined, then return undefined as the watch may be a one-time watch 28044 if (isUndefined(newValue)) return; 28045 28046 if (!isObject(newValue)) { // if primitive 28047 if (oldValue !== newValue) { 28048 oldValue = newValue; 28049 changeDetected++; 28050 } 28051 } else if (isArrayLike(newValue)) { 28052 if (oldValue !== internalArray) { 28053 // we are transitioning from something which was not an array into array. 28054 oldValue = internalArray; 28055 oldLength = oldValue.length = 0; 28056 changeDetected++; 28057 } 28058 28059 newLength = newValue.length; 28060 28061 if (oldLength !== newLength) { 28062 // if lengths do not match we need to trigger change notification 28063 changeDetected++; 28064 oldValue.length = oldLength = newLength; 28065 } 28066 // copy the items to oldValue and look for changes. 28067 for (var i = 0; i < newLength; i++) { 28068 oldItem = oldValue[i]; 28069 newItem = newValue[i]; 28070 28071 // eslint-disable-next-line no-self-compare 28072 bothNaN = (oldItem !== oldItem) && (newItem !== newItem); 28073 if (!bothNaN && (oldItem !== newItem)) { 28074 changeDetected++; 28075 oldValue[i] = newItem; 28076 } 28077 } 28078 } else { 28079 if (oldValue !== internalObject) { 28080 // we are transitioning from something which was not an object into object. 28081 oldValue = internalObject = {}; 28082 oldLength = 0; 28083 changeDetected++; 28084 } 28085 // copy the items to oldValue and look for changes. 28086 newLength = 0; 28087 for (key in newValue) { 28088 if (hasOwnProperty.call(newValue, key)) { 28089 newLength++; 28090 newItem = newValue[key]; 28091 oldItem = oldValue[key]; 28092 28093 if (key in oldValue) { 28094 // eslint-disable-next-line no-self-compare 28095 bothNaN = (oldItem !== oldItem) && (newItem !== newItem); 28096 if (!bothNaN && (oldItem !== newItem)) { 28097 changeDetected++; 28098 oldValue[key] = newItem; 28099 } 28100 } else { 28101 oldLength++; 28102 oldValue[key] = newItem; 28103 changeDetected++; 28104 } 28105 } 28106 } 28107 if (oldLength > newLength) { 28108 // we used to have more keys, need to find them and destroy them. 28109 changeDetected++; 28110 for (key in oldValue) { 28111 if (!hasOwnProperty.call(newValue, key)) { 28112 oldLength--; 28113 delete oldValue[key]; 28114 } 28115 } 28116 } 28117 } 28118 return changeDetected; 28119 } 28120 28121 function $watchCollectionAction() { 28122 if (initRun) { 28123 initRun = false; 28124 listener(newValue, newValue, self); 28125 } else { 28126 listener(newValue, veryOldValue, self); 28127 } 28128 28129 // make a copy for the next time a collection is changed 28130 if (trackVeryOldValue) { 28131 if (!isObject(newValue)) { 28132 //primitive 28133 veryOldValue = newValue; 28134 } else if (isArrayLike(newValue)) { 28135 veryOldValue = new Array(newValue.length); 28136 for (var i = 0; i < newValue.length; i++) { 28137 veryOldValue[i] = newValue[i]; 28138 } 28139 } else { // if object 28140 veryOldValue = {}; 28141 for (var key in newValue) { 28142 if (hasOwnProperty.call(newValue, key)) { 28143 veryOldValue[key] = newValue[key]; 28144 } 28145 } 28146 } 28147 } 28148 } 28149 28150 return this.$watch(changeDetector, $watchCollectionAction); 28151 }, 28152 28153 /** 28154 * @ngdoc method 28155 * @name $rootScope.Scope#$digest 28156 * @kind function 28157 * 28158 * @description 28159 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and 28160 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change 28161 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} 28162 * until no more listeners are firing. This means that it is possible to get into an infinite 28163 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of 28164 * iterations exceeds 10. 28165 * 28166 * Usually, you don't call `$digest()` directly in 28167 * {@link ng.directive:ngController controllers} or in 28168 * {@link ng.$compileProvider#directive directives}. 28169 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within 28170 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`. 28171 * 28172 * If you want to be notified whenever `$digest()` is called, 28173 * you can register a `watchExpression` function with 28174 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. 28175 * 28176 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. 28177 * 28178 *
vendor: 9,120 bytes, lines 28178-28401
28178# Example 28179 * ```js 28180 var scope = ...; 28181 scope.name = 'misko'; 28182 scope.counter = 0; 28183 28184 expect(scope.counter).toEqual(0); 28185 scope.$watch('name', function(newValue, oldValue) { 28186 scope.counter = scope.counter + 1; 28187 }); 28188 expect(scope.counter).toEqual(0); 28189 28190 scope.$digest(); 28191 // the listener is always called during the first $digest loop after it was registered 28192 expect(scope.counter).toEqual(1); 28193 28194 scope.$digest(); 28195 // but now it will not be called unless the value changes 28196 expect(scope.counter).toEqual(1); 28197 28198 scope.name = 'adam'; 28199 scope.$digest(); 28200 expect(scope.counter).toEqual(2); 28201 * ``` 28202 * 28203 */ 28204 $digest: function() { 28205 var watch, value, last, fn, get, 28206 watchers, 28207 dirty, ttl = TTL, 28208 next, current, target = this, 28209 watchLog = [], 28210 logIdx, asyncTask; 28211 28212 beginPhase('$digest'); 28213 // Check for changes to browser url that happened in sync before the call to $digest 28214 $browser.$$checkUrlChange(); 28215 28216 if (this === $rootScope && applyAsyncId !== null) { 28217 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then 28218 // cancel the scheduled $apply and flush the queue of expressions to be evaluated. 28219 $browser.defer.cancel(applyAsyncId); 28220 flushApplyAsync(); 28221 } 28222 28223 lastDirtyWatch = null; 28224 28225 do { // "while dirty" loop 28226 dirty = false; 28227 current = target; 28228 28229 // It's safe for asyncQueuePosition to be a local variable here because this loop can't 28230 // be reentered recursively. Calling $digest from a function passed to $evalAsync would 28231 // lead to a '$digest already in progress' error. 28232 for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) { 28233 try { 28234 asyncTask = asyncQueue[asyncQueuePosition]; 28235 fn = asyncTask.fn; 28236 fn(asyncTask.scope, asyncTask.locals); 28237 } catch (e) { 28238 $exceptionHandler(e); 28239 } 28240 lastDirtyWatch = null; 28241 } 28242 asyncQueue.length = 0; 28243 28244 traverseScopesLoop: 28245 do { // "traverse the scopes" loop 28246 if ((watchers = current.$$watchers)) { 28247 // process our watches 28248 watchers.$$digestWatchIndex = watchers.length; 28249 while (watchers.$$digestWatchIndex--) { 28250 try { 28251 watch = watchers[watchers.$$digestWatchIndex]; 28252 // Most common watches are on primitives, in which case we can short 28253 // circuit it with === operator, only when === fails do we use .equals 28254 if (watch) { 28255 get = watch.get; 28256 if ((value = get(current)) !== (last = watch.last) && 28257 !(watch.eq 28258 ? equals(value, last) 28259 : (isNumberNaN(value) && isNumberNaN(last)))) { 28260 dirty = true; 28261 lastDirtyWatch = watch; 28262 watch.last = watch.eq ? copy(value, null) : value; 28263 fn = watch.fn; 28264 fn(value, ((last === initWatchVal) ? value : last), current); 28265 if (ttl < 5) { 28266 logIdx = 4 - ttl; 28267 if (!watchLog[logIdx]) watchLog[logIdx] = []; 28268 watchLog[logIdx].push({ 28269 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, 28270 newVal: value, 28271 oldVal: last 28272 }); 28273 } 28274 } else if (watch === lastDirtyWatch) { 28275 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers 28276 // have already been tested. 28277 dirty = false; 28278 break traverseScopesLoop; 28279 } 28280 } 28281 } catch (e) { 28282 $exceptionHandler(e); 28283 } 28284 } 28285 } 28286 28287 // Insanity Warning: scope depth-first traversal 28288 // yes, this code is a bit crazy, but it works and we have tests to prove it! 28289 // this piece should be kept in sync with the traversal in $broadcast 28290 if (!(next = ((current.$$watchersCount && current.$$childHead) || 28291 (current !== target && current.$$nextSibling)))) { 28292 while (current !== target && !(next = current.$$nextSibling)) { 28293 current = current.$parent; 28294 } 28295 } 28296 } while ((current = next)); 28297 28298 // `break traverseScopesLoop;` takes us to here 28299 28300 if ((dirty || asyncQueue.length) && !(ttl--)) { 28301 clearPhase(); 28302 throw $rootScopeMinErr('infdig', 28303 '{0} $digest() iterations reached. Aborting!\n' + 28304 'Watchers fired in the last 5 iterations: {1}', 28305 TTL, watchLog); 28306 } 28307 28308 } while (dirty || asyncQueue.length); 28309 28310 clearPhase(); 28311 28312 // postDigestQueuePosition isn't local here because this loop can be reentered recursively. 28313 while (postDigestQueuePosition < postDigestQueue.length) { 28314 try { 28315 postDigestQueue[postDigestQueuePosition++](); 28316 } catch (e) { 28317 $exceptionHandler(e); 28318 } 28319 } 28320 postDigestQueue.length = postDigestQueuePosition = 0; 28321 28322 // Check for changes to browser url that happened during the $digest 28323 // (for which no event is fired; e.g. via `history.pushState()`) 28324 $browser.$$checkUrlChange(); 28325 }, 28326 28327 28328 /** 28329 * @ngdoc event 28330 * @name $rootScope.Scope#$destroy 28331 * @eventType broadcast on scope being destroyed 28332 * 28333 * @description 28334 * Broadcasted when a scope and its children are being destroyed. 28335 * 28336 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to 28337 * clean up DOM bindings before an element is removed from the DOM. 28338 */ 28339 28340 /** 28341 * @ngdoc method 28342 * @name $rootScope.Scope#$destroy 28343 * @kind function 28344 * 28345 * @description 28346 * Removes the current scope (and all of its children) from the parent scope. Removal implies 28347 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer 28348 * propagate to the current scope and its children. Removal also implies that the current 28349 * scope is eligible for garbage collection. 28350 * 28351 * The `$destroy()` is usually used by directives such as 28352 * {@link ng.directive:ngRepeat ngRepeat} for managing the 28353 * unrolling of the loop. 28354 * 28355 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. 28356 * Application code can register a `$destroy` event handler that will give it a chance to 28357 * perform any necessary cleanup. 28358 * 28359 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to 28360 * clean up DOM bindings before an element is removed from the DOM. 28361 */ 28362 $destroy: function() { 28363 // We can't destroy a scope that has been already destroyed. 28364 if (this.$$destroyed) return; 28365 var parent = this.$parent; 28366 28367 this.$broadcast('$destroy'); 28368 this.$$destroyed = true; 28369 28370 if (this === $rootScope) { 28371 //Remove handlers attached to window when $rootScope is removed 28372 $browser.$$applicationDestroyed(); 28373 } 28374 28375 incrementWatchersCount(this, -this.$$watchersCount); 28376 for (var eventName in this.$$listenerCount) { 28377 decrementListenerCount(this, this.$$listenerCount[eventName], eventName); 28378 } 28379 28380 // sever all the references to parent scopes (after this cleanup, the current scope should 28381 // not be retained by any of our references and should be eligible for garbage collection) 28382 if (parent && parent.$$childHead === this) parent.$$childHead = this.$$nextSibling; 28383 if (parent && parent.$$childTail === this) parent.$$childTail = this.$$prevSibling; 28384 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; 28385 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; 28386 28387 // Disable listeners, watchers and apply/digest methods 28388 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop; 28389 this.$on = this.$watch = this.$watchGroup = function() { return noop; }; 28390 this.$$listeners = {}; 28391 28392 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too 28393 this.$$nextSibling = null; 28394 cleanUpScope(this); 28395 }, 28396 28397 /** 28398 * @ngdoc method 28399 * @name $rootScope.Scope#$eval 28400 * @kind function 28401 *
28402 * @description 28403 * Executes the `expression` on the current scope and returns the result. Any exceptions in 28404 * the expression are propagated (uncaught). This is useful when evaluating Angular 28405 * expressions. 28406 * 28407 * # Example 28408 * ```js 28409 var scope = ng.$rootScope.Scope(); 28410 scope.a = 1; 28411 scope.b = 2; 28412 28413 expect(scope.$eval('a+b')).toEqual(3); 28414 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); 28415 * ``` 28416 * 28417 * @param {(string|function())=} expression An angular expression to be executed. 28418 * 28419 * - `string`: execute using the rules as defined in {@link guide/expression expression}. 28420 * - `function(scope)`: execute the function with the current `scope` parameter. 28421 * 28422 * @param {(object)=} locals Local variables object, useful for overriding values in scope. 28423 * @returns {*} The result of evaluating the expression. 28424 */ 28425 $eval: function(expr, locals) { 28426 return $parse(expr)(this, locals); 28427 }, 28428 28429 /** 28430 * @ngdoc method 28431 * @name $rootScope.Scope#$evalAsync 28432 * @kind function 28433 * 28434 * @description 28435 * Executes the expression on the current scope at a later point in time. 28436 * 28437 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only 28438 * that: 28439 * 28440 * - it will execute after the function that scheduled the evaluation (preferably before DOM 28441 * rendering). 28442 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after 28443 * `expression` execution. 28444 * 28445 * Any exceptions from the execution of the expression are forwarded to the 28446 * {@link ng.$exceptionHandler $exceptionHandler} service. 28447 * 28448 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle 28449 * will be scheduled. However, it is encouraged to always call code that changes the model 28450 * from within an `$apply` call. That includes code evaluated via `$evalAsync`. 28451 * 28452 * @param {(string|function())=} expression An angular expression to be executed. 28453 * 28454 * - `string`: execute using the rules as defined in {@link guide/expression expression}. 28455 * - `function(scope)`: execute the function with the current `scope` parameter. 28456 * 28457 * @param {(object)=} locals Local variables object, useful for overriding values in scope. 28458 */ 28459 $evalAsync: function(expr, locals) { 28460 // if we are outside of an $digest loop and this is the first time we are scheduling async 28461 // task also schedule async auto-flush 28462 if (!$rootScope.$$phase && !asyncQueue.length) { 28463 $browser.defer(function() { 28464 if (asyncQueue.length) { 28465 $rootScope.$digest(); 28466 } 28467 }); 28468 } 28469 28470 asyncQueue.push({scope: this, fn: $parse(expr), locals: locals}); 28471 }, 28472 28473 $$postDigest: function(fn) { 28474 postDigestQueue.push(fn); 28475 }, 28476 28477 /** 28478 * @ngdoc method 28479 * @name $rootScope.Scope#$apply 28480 * @kind function 28481 * 28482 * @description 28483 * `$apply()` is used to execute an expression in angular from outside of the angular 28484 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). 28485 * Because we are calling into the angular framework we need to perform proper scope life 28486 * cycle of {@link ng.$exceptionHandler exception handling}, 28487 * {@link ng.$rootScope.Scope#$digest executing watches}. 28488 * 28489 * ## Life cycle 28490 * 28491 * # Pseudo-Code of `$apply()` 28492 * ```js 28493 function $apply(expr) { 28494 try { 28495 return $eval(expr); 28496 } catch (e) { 28497 $exceptionHandler(e); 28498 } finally { 28499 $root.$digest(); 28500 } 28501 } 28502 * ``` 28503 * 28504 * 28505 * Scope's `$apply()` method transitions through the following stages: 28506 * 28507 * 1. The {@link guide/expression expression} is executed using the 28508 * {@link ng.$rootScope.Scope#$eval $eval()} method. 28509 * 2. Any exceptions from the execution of the expression are forwarded to the 28510 * {@link ng.$exceptionHandler $exceptionHandler} service.
28511 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the 28512 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. 28513 * 28514 * 28515 * @param {(string|function())=} exp An angular expression to be executed. 28516 * 28517 * - `string`: execute using the rules as defined in {@link guide/expression expression}. 28518 * - `function(scope)`: execute the function with current `scope` parameter. 28519 * 28520 * @returns {*} The result of evaluating the expression. 28521 */ 28522 $apply: function(expr) { 28523 try { 28524 beginPhase('$apply'); 28525 try { 28526 return this.$eval(expr); 28527 } finally { 28528 clearPhase(); 28529 } 28530 } catch (e) { 28531 $exceptionHandler(e); 28532 } finally { 28533 try { 28534 $rootScope.$digest(); 28535 } catch (e) { 28536 $exceptionHandler(e); 28537 // eslint-disable-next-line no-unsafe-finally 28538 throw e; 28539 } 28540 } 28541 }, 28542 28543 /** 28544 * @ngdoc method 28545 * @name $rootScope.Scope#$applyAsync 28546 * @kind function 28547 * 28548 * @description 28549 * Schedule the invocation of $apply to occur at a later time. The actual time difference 28550 * varies across browsers, but is typically around ~10 milliseconds. 28551 * 28552 * This can be used to queue up multiple expressions which need to be evaluated in the same 28553 * digest. 28554 * 28555 * @param {(string|function())=} exp An angular expression to be executed. 28556 * 28557 * - `string`: execute using the rules as defined in {@link guide/expression expression}. 28558 * - `function(scope)`: execute the function with current `scope` parameter. 28559 */ 28560 $applyAsync: function(expr) { 28561 var scope = this; 28562 if (expr) { 28563 applyAsyncQueue.push($applyAsyncExpression); 28564 } 28565 expr = $parse(expr); 28566 scheduleApplyAsync(); 28567 28568 function $applyAsyncExpression() { 28569 scope.$eval(expr); 28570 } 28571 }, 28572 28573 /** 28574 * @ngdoc method 28575 * @name $rootScope.Scope#$on 28576 * @kind function 28577 * 28578 * @description 28579 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for 28580 * discussion of event life cycle. 28581 * 28582 * The event listener function format is: `function(event, args...)`. The `event` object 28583 * passed into the listener has the following attributes: 28584 * 28585 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or 28586 * `$broadcast`-ed. 28587 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the 28588 * event propagates through the scope hierarchy, this property is set to null. 28589 * - `name` - `{string}`: name of the event. 28590 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel 28591 * further event propagation (available only for events that were `$emit`-ed). 28592 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag 28593 * to true. 28594 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. 28595 * 28596 * @param {string} name Event name to listen on. 28597 * @param {function(event, ...args)} listener Function to call when the event is emitted. 28598 * @returns {function()} Returns a deregistration function for this listener. 28599 */ 28600 $on: function(name, listener) { 28601 var namedListeners = this.$$listeners[name]; 28602 if (!namedListeners) { 28603 this.$$listeners[name] = namedListeners = []; 28604 } 28605 namedListeners.push(listener); 28606 28607 var current = this; 28608 do { 28609 if (!current.$$listenerCount[name]) { 28610 current.$$listenerCount[name] = 0; 28611 } 28612 current.$$listenerCount[name]++; 28613 } while ((current = current.$parent)); 28614 28615 var self = this; 28616 return function() { 28617 var indexOfListener = namedListeners.indexOf(listener); 28618 if (indexOfListener !== -1) { 28619 namedListeners[indexOfListener] = null; 28620 decrementListenerCount(self, 1, name); 28621 } 28622 }; 28623 }, 28624 28625 28626 /** 28627 * @ngdoc method 28628 * @name $rootScope.Scope#$emit 28629 * @kind function 28630 *
28631 * @description 28632 * Dispatches an event `name` upwards through the scope hierarchy notifying the 28633 * registered {@link ng.$rootScope.Scope#$on} listeners. 28634 * 28635 * The event life cycle starts at the scope on which `$emit` was called. All 28636 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get 28637 * notified. Afterwards, the event traverses upwards toward the root scope and calls all 28638 * registered listeners along the way. The event will stop propagating if one of the listeners 28639 * cancels it. 28640 * 28641 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed 28642 * onto the {@link ng.$exceptionHandler $exceptionHandler} service. 28643 * 28644 * @param {string} name Event name to emit. 28645 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. 28646 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). 28647 */ 28648 $emit: function(name, args) { 28649 var empty = [], 28650 namedListeners, 28651 scope = this, 28652 stopPropagation = false, 28653 event = { 28654 name: name, 28655 targetScope: scope, 28656 stopPropagation: function() {stopPropagation = true;}, 28657 preventDefault: function() { 28658 event.defaultPrevented = true; 28659 }, 28660 defaultPrevented: false 28661 }, 28662 listenerArgs = concat([event], arguments, 1), 28663 i, length; 28664 28665 do { 28666 namedListeners = scope.$$listeners[name] || empty; 28667 event.currentScope = scope; 28668 for (i = 0, length = namedListeners.length; i < length; i++) { 28669 28670 // if listeners were deregistered, defragment the array 28671 if (!namedListeners[i]) { 28672 namedListeners.splice(i, 1); 28673 i--; 28674 length--; 28675 continue; 28676 } 28677 try { 28678 //allow all listeners attached to the current scope to run 28679 namedListeners[i].apply(null, listenerArgs); 28680 } catch (e) { 28681 $exceptionHandler(e); 28682 } 28683 } 28684 //if any listener on the current scope stops propagation, prevent bubbling 28685 if (stopPropagation) { 28686 event.currentScope = null; 28687 return event; 28688 } 28689 //traverse upwards 28690 scope = scope.$parent; 28691 } while (scope); 28692 28693 event.currentScope = null; 28694 28695 return event; 28696 }, 28697 28698 28699 /** 28700 * @ngdoc method 28701 * @name $rootScope.Scope#$broadcast 28702 * @kind function 28703 * 28704 * @description 28705 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the 28706 * registered {@link ng.$rootScope.Scope#$on} listeners. 28707 * 28708 * The event life cycle starts at the scope on which `$broadcast` was called. All 28709 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get 28710 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current 28711 * scope and calls all registered listeners along the way. The event cannot be canceled. 28712 * 28713 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed 28714 * onto the {@link ng.$exceptionHandler $exceptionHandler} service. 28715 * 28716 * @param {string} name Event name to broadcast. 28717 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. 28718 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} 28719 */ 28720 $broadcast: function(name, args) { 28721 var target = this, 28722 current = target, 28723 next = target, 28724 event = { 28725 name: name, 28726 targetScope: target, 28727 preventDefault: function() { 28728 event.defaultPrevented = true; 28729 }, 28730 defaultPrevented: false 28731 }; 28732 28733 if (!target.$$listenerCount[name]) return event; 28734 28735 var listenerArgs = concat([event], arguments, 1), 28736 listeners, i, length; 28737 28738 //down while you can, then up and next sibling or up and next sibling until back at root 28739 while ((current = next)) { 28740 event.currentScope = current; 28741 listeners = current.$$listeners[name] || []; 28742 for (i = 0, length = listeners.length; i < length; i++) { 28743 // if listeners were deregistered, defragment the array 28744 if (!listeners[i]) { 28745 listeners.splice(i, 1); 28746 i--; 28747 length--; 28748 continue; 28749 } 28750 28751 try { 28752 listeners[i].apply(null, listenerArgs); 28753 } catch (e) { 28754 $exceptionHandler(e); 28755 } 28756 } 28757 28758 // Insanity Warning: scope depth-first traversal 28759 // yes, this code is a bit crazy, but it works and we have tests to prove it! 28760 // this piece should be kept in sync with the traversal in $digest 28761 // (though it differs due to having the extra check for $$listenerCount) 28762 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || 28763 (current !== target && current.$$nextSibling)))) { 28764 while (current !== target && !(next = current.$$nextSibling)) { 28765 current = current.$parent; 28766 } 28767 } 28768 } 28769 28770 event.currentScope = null; 28771 return event; 28772 } 28773 }; 28774 28775 var $rootScope = new Scope(); 28776 28777 //The internal queues. Expose them on the $rootScope for debugging/testing purposes. 28778 var asyncQueue = $rootScope.$$asyncQueue = []; 28779 var postDigestQueue = $rootScope.$$postDigestQueue = []; 28780 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = []; 28781 28782 var postDigestQueuePosition = 0; 28783 28784 return $rootScope; 28785 28786 28787 function beginPhase(phase) { 28788 if ($rootScope.$$phase) { 28789 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase); 28790 } 28791 28792 $rootScope.$$phase = phase; 28793 } 28794 28795 function clearPhase() { 28796 $rootScope.$$phase = null; 28797 } 28798 28799 function incrementWatchersCount(current, count) { 28800 do { 28801 current.$$watchersCount += count; 28802 } while ((current = current.$parent)); 28803 } 28804 28805 function decrementListenerCount(current, count, name) { 28806 do { 28807 current.$$listenerCount[name] -= count; 28808 28809 if (current.$$listenerCount[name] === 0) { 28810 delete current.$$listenerCount[name]; 28811 } 28812 } while ((current = current.$parent)); 28813 } 28814 28815 /** 28816 * function used as an initial value for watchers. 28817 * because it's unique we can easily tell it apart from other values 28818 */ 28819 function initWatchVal() {} 28820 28821 function flushApplyAsync() {
vendor: 4,475 bytes, lines 28822-28938
28822 while (applyAsyncQueue.length) { 28823 try { 28824 applyAsyncQueue.shift()(); 28825 } catch (e) { 28826 $exceptionHandler(e); 28827 } 28828 } 28829 applyAsyncId = null; 28830 } 28831 28832 function scheduleApplyAsync() { 28833 if (applyAsyncId === null) { 28834 applyAsyncId = $browser.defer(function() { 28835 $rootScope.$apply(flushApplyAsync); 28836 }); 28837 } 28838 } 28839 }]; 28840} 28841 28842/** 28843 * @ngdoc service 28844 * @name $rootElement 28845 * 28846 * @description 28847 * The root element of Angular application. This is either the element where {@link 28848 * ng.directive:ngApp ngApp} was declared or the element passed into 28849 * {@link angular.bootstrap}. The element represents the root element of application. It is also the 28850 * location where the application's {@link auto.$injector $injector} service gets 28851 * published, and can be retrieved using `$rootElement.injector()`. 28852 */ 28853 28854 28855// the implementation is in angular.bootstrap 28856 28857/** 28858 * @this 28859 * @description 28860 * Private service to sanitize uris for links and images. Used by $compile and $sanitize. 28861 */ 28862function $$SanitizeUriProvider() { 28863 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/, 28864 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/; 28865 28866 /** 28867 * @description 28868 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 28869 * urls during a[href] sanitization. 28870 * 28871 * The sanitization is a security measure aimed at prevent XSS attacks via html links. 28872 * 28873 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into 28874 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` 28875 * regular expression. If a match is found, the original url is written into the dom. Otherwise, 28876 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 28877 * 28878 * @param {RegExp=} regexp New regexp to whitelist urls with. 28879 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 28880 * chaining otherwise. 28881 */ 28882 this.aHrefSanitizationWhitelist = function(regexp) { 28883 if (isDefined(regexp)) { 28884 aHrefSanitizationWhitelist = regexp; 28885 return this; 28886 } 28887 return aHrefSanitizationWhitelist; 28888 }; 28889 28890 28891 /** 28892 * @description 28893 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 28894 * urls during img[src] sanitization. 28895 * 28896 * The sanitization is a security measure aimed at prevent XSS attacks via html links. 28897 * 28898 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into 28899 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` 28900 * regular expression. If a match is found, the original url is written into the dom. Otherwise, 28901 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 28902 * 28903 * @param {RegExp=} regexp New regexp to whitelist urls with. 28904 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 28905 * chaining otherwise. 28906 */ 28907 this.imgSrcSanitizationWhitelist = function(regexp) { 28908 if (isDefined(regexp)) { 28909 imgSrcSanitizationWhitelist = regexp; 28910 return this; 28911 } 28912 return imgSrcSanitizationWhitelist; 28913 }; 28914 28915 this.$get = function() { 28916 return function sanitizeUri(uri, isImage) { 28917 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; 28918 var normalizedVal; 28919 normalizedVal = urlResolve(uri).href; 28920 if (normalizedVal !== '' && !normalizedVal.match(regex)) { 28921 return 'unsafe:' + normalizedVal; 28922 } 28923 return uri; 28924 }; 28925 }; 28926} 28927 28928/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 28929 * Any commits to this file should be reviewed with security in mind. * 28930 * Changes to this file can potentially create security vulnerabilities. * 28931 * An approval from 2 Core members with history of modifying * 28932 * this file is required. * 28933 * * 28934 * Does the change somehow allow for arbitrary javascript to be executed? * 28935 * Or allows for someone to change the prototype of built-in objects? * 28936 * Or gives undesired access to variables likes document or window? * 28937 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 28938
28939/* exported $SceProvider, $SceDelegateProvider */ 28940 28941var $sceMinErr = minErr('$sce'); 28942 28943var SCE_CONTEXTS = { 28944 HTML: 'html', 28945 CSS: 'css', 28946 URL: 'url', 28947 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a 28948 // url. (e.g. ng-include, script src, templateUrl) 28949 RESOURCE_URL: 'resourceUrl', 28950 JS: 'js' 28951}; 28952 28953// Helper functions follow. 28954 28955var UNDERSCORE_LOWERCASE_REGEXP = /_([a-z])/g; 28956 28957function snakeToCamel(name) { 28958 return name 28959 .replace(UNDERSCORE_LOWERCASE_REGEXP, fnCamelCaseReplace); 28960} 28961 28962function adjustMatcher(matcher) { 28963 if (matcher === 'self') { 28964 return matcher; 28965 } else if (isString(matcher)) { 28966 // Strings match exactly except for 2 wildcards - '*' and '**'. 28967 // '*' matches any character except those from the set ':/.?&'. 28968 // '**' matches any character (like .* in a RegExp). 28969 // More than 2 *'s raises an error as it's ill defined. 28970 if (matcher.indexOf('***') > -1) { 28971 throw $sceMinErr('iwcard', 28972 'Illegal sequence *** in string matcher. String: {0}', matcher); 28973 } 28974 matcher = escapeForRegexp(matcher).
28975 replace(/\\\*\\\*/g, '.*'). 28976 replace(/\\\*/g, '[^:/.?&;]*'); 28977 return new RegExp('^' + matcher + '$'); 28978 } else if (isRegExp(matcher)) { 28979 // The only other type of matcher allowed is a Regexp. 28980 // Match entire URL / disallow partial matches. 28981 // Flags are reset (i.e. no global, ignoreCase or multiline) 28982 return new RegExp('^' + matcher.source + '$'); 28983 } else { 28984 throw $sceMinErr('imatcher', 28985 'Matchers may only be "self", string patterns or RegExp objects'); 28986 } 28987} 28988 28989 28990function adjustMatchers(matchers) { 28991 var adjustedMatchers = []; 28992 if (isDefined(matchers)) { 28993 forEach(matchers, function(matcher) { 28994 adjustedMatchers.push(adjustMatcher(matcher)); 28995 }); 28996 } 28997 return adjustedMatchers; 28998} 28999 29000 29001/** 29002 * @ngdoc service 29003 * @name $sceDelegate 29004 * @kind function 29005 * 29006 * @description 29007 * 29008 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict 29009 * Contextual Escaping (SCE)} services to AngularJS. 29010 * 29011 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of 29012 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is 29013 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to 29014 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things 29015 * work because `$sce` delegates to `$sceDelegate` for these operations. 29016 * 29017 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. 29018 * 29019 * The default instance of `$sceDelegate` should work out of the box with little pain. While you 29020 * can override it completely to change the behavior of `$sce`, the common case would 29021 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting 29022 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as 29023 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist 29024 * $sceDelegateProvider.resourceUrlWhitelist} and {@link 29025 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} 29026 */ 29027 29028/** 29029 * @ngdoc provider 29030 * @name $sceDelegateProvider 29031 * @this 29032 * 29033 * @description 29034 * 29035 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate 29036 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure 29037 * that the URLs used for sourcing Angular templates are safe. Refer {@link 29038 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and 29039 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} 29040 * 29041 * For the general details about this service in Angular, read the main page for {@link ng.$sce 29042 * Strict Contextual Escaping (SCE)}. 29043 * 29044 * **Example**: Consider the following case. <a name="example"></a> 29045 * 29046 * - your app is hosted at url `http://myapp.example.com/` 29047 * - but some of your templates are hosted on other domains you control such as 29048 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc. 29049 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. 29050 * 29051 * Here is what a secure configuration for this scenario might look like: 29052 * 29053 * ``` 29054 * angular.module('myApp', []).config(function($sceDelegateProvider) { 29055 * $sceDelegateProvider.resourceUrlWhitelist([ 29056 * // Allow same origin resource loads. 29057 * 'self', 29058 * // Allow loading from our assets domain. Notice the difference between * and **. 29059 * 'http://srv*.assets.example.com/**' 29060 * ]); 29061 * 29062 * // The blacklist overrides the whitelist so the open redirect here is blocked. 29063 * $sceDelegateProvider.resourceUrlBlacklist([ 29064 * 'http://myapp.example.com/clickThru**' 29065 * ]); 29066 * }); 29067 * ``` 29068 */ 29069 29070function $SceDelegateProvider() { 29071 this.SCE_CONTEXTS = SCE_CONTEXTS; 29072 29073 // Resource URLs can also be trusted by policy. 29074 var resourceUrlWhitelist = ['self'], 29075 resourceUrlBlacklist = []; 29076 29077 /** 29078 * @ngdoc method 29079 * @name $sceDelegateProvider#resourceUrlWhitelist 29080 * @kind function 29081 * 29082 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value 29083 * provided. This must be an array or null. A snapshot of this array is used so further 29084 * changes to the array are ignored. 29085 * 29086 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items 29087 * allowed in this array.
vendor: 4,196 bytes, lines 29088-29214
29088 * 29089 * <div class="alert alert-warning"> 29090 * **Note:** an empty whitelist array will block all URLs! 29091 * </div> 29092 * 29093 * @return {Array} the currently set whitelist array. 29094 * 29095 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only 29096 * same origin resource requests. 29097 * 29098 * @description 29099 * Sets/Gets the whitelist of trusted resource URLs. 29100 */ 29101 this.resourceUrlWhitelist = function(value) { 29102 if (arguments.length) { 29103 resourceUrlWhitelist = adjustMatchers(value); 29104 } 29105 return resourceUrlWhitelist; 29106 }; 29107 29108 /** 29109 * @ngdoc method 29110 * @name $sceDelegateProvider#resourceUrlBlacklist 29111 * @kind function 29112 * 29113 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value 29114 * provided. This must be an array or null. A snapshot of this array is used so further 29115 * changes to the array are ignored. 29116 * 29117 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items 29118 * allowed in this array. 29119 * 29120 * The typical usage for the blacklist is to **block 29121 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as 29122 * these would otherwise be trusted but actually return content from the redirected domain. 29123 * 29124 * Finally, **the blacklist overrides the whitelist** and has the final say. 29125 * 29126 * @return {Array} the currently set blacklist array. 29127 * 29128 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there 29129 * is no blacklist.) 29130 * 29131 * @description 29132 * Sets/Gets the blacklist of trusted resource URLs. 29133 */ 29134 29135 this.resourceUrlBlacklist = function(value) { 29136 if (arguments.length) { 29137 resourceUrlBlacklist = adjustMatchers(value); 29138 } 29139 return resourceUrlBlacklist; 29140 }; 29141 29142 this.$get = ['$injector', function($injector) { 29143 29144 var htmlSanitizer = function htmlSanitizer(html) { 29145 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); 29146 }; 29147 29148 if ($injector.has('$sanitize')) { 29149 htmlSanitizer = $injector.get('$sanitize'); 29150 } 29151 29152 29153 function matchUrl(matcher, parsedUrl) { 29154 if (matcher === 'self') { 29155 return urlIsSameOrigin(parsedUrl); 29156 } else { 29157 // definitely a regex. See adjustMatchers() 29158 return !!matcher.exec(parsedUrl.href); 29159 } 29160 } 29161 29162 function isResourceUrlAllowedByPolicy(url) { 29163 var parsedUrl = urlResolve(url.toString()); 29164 var i, n, allowed = false; 29165 // Ensure that at least one item from the whitelist allows this url. 29166 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { 29167 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { 29168 allowed = true; 29169 break; 29170 } 29171 } 29172 if (allowed) { 29173 // Ensure that no item from the blacklist blocked this url. 29174 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { 29175 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { 29176 allowed = false; 29177 break; 29178 } 29179 } 29180 } 29181 return allowed; 29182 } 29183 29184 function generateHolderType(Base) { 29185 var holderType = function TrustedValueHolderType(trustedValue) { 29186 this.$$unwrapTrustedValue = function() { 29187 return trustedValue; 29188 }; 29189 }; 29190 if (Base) { 29191 holderType.prototype = new Base(); 29192 } 29193 holderType.prototype.valueOf = function sceValueOf() { 29194 return this.$$unwrapTrustedValue(); 29195 }; 29196 holderType.prototype.toString = function sceToString() { 29197 return this.$$unwrapTrustedValue().toString(); 29198 }; 29199 return holderType; 29200 } 29201 29202 var trustedValueHolderBase = generateHolderType(), 29203 byType = {}; 29204 29205 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); 29206 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); 29207 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); 29208 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); 29209 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); 29210 29211 /** 29212 * @ngdoc method 29213 * @name $sceDelegate#trustAs 29214 *
29215 * @description 29216 * Returns an object that is trusted by angular for use in specified strict 29217 * contextual escaping contexts (such as ng-bind-html, ng-include, any src 29218 * attribute interpolation, any dom event binding attribute interpolation 29219 * such as for onclick, etc.) that uses the provided value. 29220 * See {@link ng.$sce $sce} for enabling strict contextual escaping. 29221 * 29222 * @param {string} type The kind of context in which this value is safe for use. e.g. url, 29223 * resourceUrl, html, js and css. 29224 * @param {*} value The value that that should be considered trusted/safe. 29225 * @returns {*} A value that can be used to stand in for the provided `value` in places 29226 * where Angular expects a $sce.trustAs() return value. 29227 */ 29228 function trustAs(type, trustedValue) { 29229 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); 29230 if (!Constructor) { 29231 throw $sceMinErr('icontext', 29232 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', 29233 type, trustedValue); 29234 } 29235 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') { 29236 return trustedValue; 29237 } 29238 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting 29239 // mutable objects, we ensure here that the value passed in is actually a string. 29240 if (typeof trustedValue !== 'string') { 29241 throw $sceMinErr('itype', 29242 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', 29243 type); 29244 } 29245 return new Constructor(trustedValue); 29246 } 29247 29248 /** 29249 * @ngdoc method 29250 * @name $sceDelegate#valueOf 29251 * 29252 * @description 29253 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs 29254 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link 29255 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. 29256 * 29257 * If the passed parameter is not a value that had been returned by {@link 29258 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. 29259 * 29260 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} 29261 * call or anything else. 29262 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs 29263 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns 29264 * `value` unchanged. 29265 */ 29266 function valueOf(maybeTrusted) { 29267 if (maybeTrusted instanceof trustedValueHolderBase) { 29268 return maybeTrusted.$$unwrapTrustedValue(); 29269 } else { 29270 return maybeTrusted; 29271 } 29272 } 29273 29274 /** 29275 * @ngdoc method 29276 * @name $sceDelegate#getTrusted 29277 * 29278 * @description 29279 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and 29280 * returns the originally supplied value if the queried context type is a supertype of the 29281 * created type. If this condition isn't satisfied, throws an exception. 29282 * 29283 * <div class="alert alert-danger"> 29284 * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting 29285 * (XSS) vulnerability in your application. 29286 * </div> 29287 * 29288 * @param {string} type The kind of context in which this value is to be used. 29289 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs 29290 * `$sceDelegate.trustAs`} call. 29291 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs 29292 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. 29293 */ 29294 function getTrusted(type, maybeTrusted) { 29295 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') { 29296 return maybeTrusted; 29297 } 29298 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); 29299 if (constructor && maybeTrusted instanceof constructor) { 29300 return maybeTrusted.$$unwrapTrustedValue(); 29301 } 29302 // If we get here, then we may only take one of two actions. 29303 // 1. sanitize the value for the requested type, or 29304 // 2. throw an exception. 29305 if (type === SCE_CONTEXTS.RESOURCE_URL) { 29306 if (isResourceUrlAllowedByPolicy(maybeTrusted)) { 29307 return maybeTrusted; 29308 } else { 29309 throw $sceMinErr('insecurl', 29310 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', 29311 maybeTrusted.toString()); 29312 } 29313 } else if (type === SCE_CONTEXTS.HTML) { 29314 return htmlSanitizer(maybeTrusted); 29315 } 29316 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); 29317 } 29318 29319 return { trustAs: trustAs, 29320 getTrusted: getTrusted,
29321 valueOf: valueOf }; 29322 }]; 29323} 29324 29325 29326/** 29327 * @ngdoc provider 29328 * @name $sceProvider 29329 * @this 29330 * 29331 * @description 29332 * 29333 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. 29334 * - enable/disable Strict Contextual Escaping (SCE) in a module 29335 * - override the default implementation with a custom delegate 29336 * 29337 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. 29338 */ 29339 29340/** 29341 * @ngdoc service 29342 * @name $sce 29343 * @kind function 29344 * 29345 * @description 29346 * 29347 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. 29348 * 29349 * # Strict Contextual Escaping 29350 * 29351 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain 29352 * contexts to result in a value that is marked as safe to use for that context. One example of 29353 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer 29354 * to these contexts as privileged or SCE contexts. 29355 * 29356 * As of version 1.2, Angular ships with SCE enabled by default. 29357 * 29358 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow 29359 * one to execute arbitrary javascript by the use of the expression() syntax. Refer 29360 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
29361 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>` 29362 * to the top of your HTML document. 29363 * 29364 * SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for 29365 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. 29366 * 29367 * Here's an example of a binding in a privileged context: 29368 * 29369 * ``` 29370 * <input ng-model="userHtml" aria-label="User input"> 29371 * <div ng-bind-html="userHtml"></div> 29372 * ``` 29373 * 29374 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE 29375 * disabled, this application allows the user to render arbitrary HTML into the DIV. 29376 * In a more realistic example, one may be rendering user comments, blog articles, etc. via 29377 * bindings. (HTML is just one example of a context where rendering user controlled input creates 29378 * security vulnerabilities.) 29379 * 29380 * For the case of HTML, you might use a library, either on the client side, or on the server side, 29381 * to sanitize unsafe HTML before binding to the value and rendering it in the document. 29382 * 29383 * How would you ensure that every place that used these types of bindings was bound to a value that 29384 * was sanitized by your library (or returned as safe for rendering by your server?) How can you 29385 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some 29386 * properties/fields and forgot to update the binding to the sanitized value? 29387 * 29388 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can 29389 * determine that something explicitly says it's safe to use a value for binding in that
29390 * context. You can then audit your code (a simple grep would do) to ensure that this is only done 29391 * for those values that you can easily tell are safe - because they were received from your server, 29392 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps 29393 * allowing only the files in a specific directory to do this. Ensuring that the internal API 29394 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. 29395 * 29396 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} 29397 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to 29398 * obtain values that will be accepted by SCE / privileged contexts. 29399 * 29400 * 29401 * ## How does it work? 29402 * 29403 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted 29404 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link 29405 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the 29406 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. 29407 * 29408 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link 29409 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly 29410 * simplified): 29411 * 29412 * ``` 29413 * var ngBindHtmlDirective = ['$sce', function($sce) { 29414 * return function(scope, element, attr) { 29415 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { 29416 * element.html(value || ''); 29417 * }); 29418 * }; 29419 * }]; 29420 * ``` 29421 * 29422 * ## Impact on loading templates 29423 * 29424 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as 29425 * `templateUrl`'s specified by {@link guide/directive directives}. 29426 * 29427 * By default, Angular only loads templates from the same domain and protocol as the application 29428 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl 29429 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or 29430 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist 29431 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. 29432 * 29433 * *Please note*: 29434 * The browser's 29435 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) 29436 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) 29437 * policy apply in addition to this and may further restrict whether the template is successfully 29438 * loaded. This means that without the right CORS policy, loading templates from a different domain 29439 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some 29440 * browsers. 29441 * 29442 * ## This feels like too much overhead 29443 * 29444 * It's important to remember that SCE only applies to interpolation expressions. 29445 * 29446 * If your expressions are constant literals, they're automatically trusted and you don't need to 29447 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. 29448 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. 29449 * 29450 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them 29451 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. 29452 * 29453 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load 29454 * templates in `ng-include` from your application's domain without having to even know about SCE. 29455 * It blocks loading templates from other domains or loading templates over http from an https 29456 * served document. You can change these by setting your own custom {@link 29457 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link 29458 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. 29459 * 29460 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an 29461 * application that's secure and can be audited to verify that with much more ease than bolting 29462 * security onto an application later. 29463 * 29464 * <a name="contexts"></a> 29465 * ## What trusted context types are supported? 29466 * 29467 * | Context | Notes | 29468 * |---------------------|----------------| 29469 * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | 29470 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | 29471 * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. | 29472 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | 29473 * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | 29474 * 29475 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a> 29476 *
29477 * Each element in these arrays must be one of the following: 29478 * 29479 * - **'self'** 29480 * - The special **string**, `'self'`, can be used to match against all URLs of the **same 29481 * domain** as the application document using the **same protocol**. 29482 * - **String** (except the special value `'self'`) 29483 * - The string is matched against the full *normalized / absolute URL* of the resource 29484 * being tested (substring matches are not good enough.) 29485 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters 29486 * match themselves. 29487 * - `*`: matches zero or more occurrences of any character other than one of the following 6 29488 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use 29489 * in a whitelist. 29490 * - `**`: matches zero or more occurrences of *any* character. As such, it's not 29491 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g. 29492 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might 29493 * not have been the intention.) Its usage at the very end of the path is ok. (e.g. 29494 * http://foo.example.com/templates/**). 29495 * - **RegExp** (*see caveat below*) 29496 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax 29497 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to 29498 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should 29499 * have good test coverage). For instance, the use of `.` in the regex is correct only in a 29500 * small number of cases. A `.` character in the regex used when matching the scheme or a 29501 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It 29502 * is highly recommended to use the string patterns and only fall back to regular expressions 29503 * as a last resort. 29504 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is 29505 * matched against the **entire** *normalized / absolute URL* of the resource being tested 29506 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags 29507 * present on the RegExp (such as multiline, global, ignoreCase) are ignored. 29508 * - If you are generating your JavaScript from some other templating engine (not 29509 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), 29510 * remember to escape your regular expression (and be aware that you might need more than 29511 * one level of escaping depending on your templating engine and the way you interpolated 29512 * the value.) Do make use of your platform's escaping mechanism as it might be good 29513 * enough before coding your own. E.g. Ruby has 29514 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) 29515 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). 29516 * Javascript lacks a similar built in function for escaping. Take a look at Google 29517 * Closure library's [goog.string.regExpEscape(s)]( 29518 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). 29519 * 29520 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. 29521 * 29522 * ## Show me an example using SCE. 29523 * 29524 * <example module="mySceApp" deps="angular-sanitize.js" name="sce-service"> 29525 * <file name="index.html"> 29526 * <div ng-controller="AppController as myCtrl"> 29527 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br> 29528 * <b>User comments</b><br> 29529 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when 29530 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an 29531 * exploit. 29532 * <div class="well"> 29533 * <div ng-repeat="userComment in myCtrl.userComments"> 29534 * <b>{{userComment.name}}</b>: 29535 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span> 29536 * <br> 29537 * </div>
29538 * </div> 29539 * </div> 29540 * </file> 29541 * 29542 * <file name="script.js"> 29543 * angular.module('mySceApp', ['ngSanitize']) 29544 * .controller('AppController', ['$http', '$templateCache', '$sce', 29545 * function AppController($http, $templateCache, $sce) { 29546 * var self = this; 29547 * $http.get('test_data.json', {cache: $templateCache}).then(function(response) { 29548 * self.userComments = response.data; 29549 * }); 29550 * self.explicitlyTrustedHtml = $sce.trustAsHtml( 29551 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + 29552 * 'sanitization."">Hover over this text.</span>'); 29553 * }]); 29554 * </file> 29555 * 29556 * <file name="test_data.json"> 29557 * [ 29558 * { "name": "Alice", 29559 * "htmlComment": 29560 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>" 29561 * }, 29562 * { "name": "Bob", 29563 * "htmlComment": "<i>Yes!</i> Am I the only other one?" 29564 * } 29565 * ] 29566 * </file> 29567 * 29568 * <file name="protractor.js" type="protractor"> 29569 * describe('SCE doc demo', function() { 29570 * it('should sanitize untrusted values', function() { 29571 * expect(element.all(by.css('.htmlComment')).first().getAttribute('innerHTML')) 29572 * .toBe('<span>Is <i>anyone</i> reading this?</span>'); 29573 * }); 29574 * 29575 * it('should NOT sanitize explicitly trusted values', function() { 29576 * expect(element(by.id('explicitlyTrustedHtml')).getAttribute('innerHTML')).toBe( 29577 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + 29578 * 'sanitization."">Hover over this text.</span>'); 29579 * }); 29580 * }); 29581 * </file> 29582 * </example> 29583 * 29584 * 29585 * 29586 * ## Can I disable SCE completely? 29587 * 29588 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits 29589 * for little coding overhead. It will be much harder to take an SCE disabled application and 29590 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE 29591 * for cases where you have a lot of existing code that was written before SCE was introduced and 29592 * you're migrating them a module at a time. 29593 * 29594 * That said, here's how you can completely disable SCE: 29595 * 29596 * ``` 29597 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { 29598 * // Completely disable SCE. For demonstration purposes only! 29599 * // Do not use in new projects. 29600 * $sceProvider.enabled(false); 29601 * }); 29602 * ``` 29603 * 29604 */ 29605 29606function $SceProvider() { 29607 var enabled = true; 29608 29609 /** 29610 * @ngdoc method 29611 * @name $sceProvider#enabled 29612 * @kind function 29613 * 29614 * @param {boolean=} value If provided, then enables/disables SCE. 29615 * @return {boolean} true if SCE is enabled, false otherwise. 29616 * 29617 * @description 29618 * Enables/disables SCE and returns the current value. 29619 */ 29620 this.enabled = function(value) { 29621 if (arguments.length) { 29622 enabled = !!value; 29623 } 29624 return enabled; 29625 }; 29626 29627 29628 /* Design notes on the default implementation for SCE. 29629 * 29630 * The API contract for the SCE delegate 29631 * ------------------------------------- 29632 * The SCE delegate object must provide the following 3 methods: 29633 * 29634 * - trustAs(contextEnum, value) 29635 * This method is used to tell the SCE service that the provided value is OK to use in the 29636 * contexts specified by contextEnum. It must return an object that will be accepted by 29637 * getTrusted() for a compatible contextEnum and return this value. 29638 * 29639 * - valueOf(value) 29640 * For values that were not produced by trustAs(), return them as is. For values that were 29641 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if 29642 * trustAs is wrapping the given values into some type, this operation unwraps it when given 29643 * such a value. 29644 * 29645 * - getTrusted(contextEnum, value) 29646 * This function should return the a value that is safe to use in the context specified by 29647 * contextEnum or throw and exception otherwise. 29648 * 29649 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be 29650 * opaque or wrapped in some holder object. That happens to be an implementation detail. For 29651 * instance, an implementation could maintain a registry of all trusted objects by context. In 29652 * such a case, trustAs() would return the same object that was passed in. getTrusted() would 29653 * return the same object passed in if it was found in the registry under a compatible context or 29654 * throw an exception otherwise. An implementation might only wrap values some of the time based 29655 * on some criteria. getTrusted() might return a value and not throw an exception for special 29656 * constants or objects even if not wrapped. All such implementations fulfill this contract. 29657 * 29658 * 29659 * A note on the inheritance model for SCE contexts 29660 * ------------------------------------------------ 29661 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This 29662 * is purely an implementation details. 29663 * 29664 * The contract is simply this: 29665 * 29666 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) 29667 * will also succeed. 29668 * 29669 * Inheritance happens to capture this in a natural way. In some future, we
vendor: 4,370 bytes, lines 29670-29778
29670 * may not use inheritance anymore. That is OK because no code outside of 29671 * sce.js and sceSpecs.js would need to be aware of this detail. 29672 */ 29673 29674 this.$get = ['$parse', '$sceDelegate', function( 29675 $parse, $sceDelegate) { 29676 // Support: IE 9-11 only 29677 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow 29678 // the "expression(javascript expression)" syntax which is insecure. 29679 if (enabled && msie < 8) { 29680 throw $sceMinErr('iequirks', 29681 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' + 29682 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' + 29683 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); 29684 } 29685 29686 var sce = shallowCopy(SCE_CONTEXTS); 29687 29688 /** 29689 * @ngdoc method 29690 * @name $sce#isEnabled 29691 * @kind function 29692 * 29693 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you 29694 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. 29695 * 29696 * @description 29697 * Returns a boolean indicating if SCE is enabled. 29698 */ 29699 sce.isEnabled = function() { 29700 return enabled; 29701 }; 29702 sce.trustAs = $sceDelegate.trustAs; 29703 sce.getTrusted = $sceDelegate.getTrusted; 29704 sce.valueOf = $sceDelegate.valueOf; 29705 29706 if (!enabled) { 29707 sce.trustAs = sce.getTrusted = function(type, value) { return value; }; 29708 sce.valueOf = identity; 29709 } 29710 29711 /** 29712 * @ngdoc method 29713 * @name $sce#parseAs 29714 * 29715 * @description 29716 * Converts Angular {@link guide/expression expression} into a function. This is like {@link 29717 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it 29718 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, 29719 * *result*)} 29720 * 29721 * @param {string} type The kind of SCE context in which this result will be used. 29722 * @param {string} expression String expression to compile. 29723 * @returns {function(context, locals)} a function which represents the compiled expression: 29724 * 29725 * * `context` – `{object}` – an object against which any expressions embedded in the strings 29726 * are evaluated against (typically a scope object). 29727 * * `locals` – `{object=}` – local variables context object, useful for overriding values in 29728 * `context`. 29729 */ 29730 sce.parseAs = function sceParseAs(type, expr) { 29731 var parsed = $parse(expr); 29732 if (parsed.literal && parsed.constant) { 29733 return parsed; 29734 } else { 29735 return $parse(expr, function(value) { 29736 return sce.getTrusted(type, value); 29737 }); 29738 } 29739 }; 29740 29741 /** 29742 * @ngdoc method 29743 * @name $sce#trustAs 29744 * 29745 * @description 29746 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, 29747 * returns an object that is trusted by angular for use in specified strict contextual 29748 * escaping contexts (such as ng-bind-html, ng-include, any src attribute 29749 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) 29750 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual 29751 * escaping. 29752 * 29753 * @param {string} type The kind of context in which this value is safe for use. e.g. url, 29754 * resourceUrl, html, js and css. 29755 * @param {*} value The value that that should be considered trusted/safe. 29756 * @returns {*} A value that can be used to stand in for the provided `value` in places 29757 * where Angular expects a $sce.trustAs() return value. 29758 */ 29759 29760 /** 29761 * @ngdoc method 29762 * @name $sce#trustAsHtml 29763 * 29764 * @description 29765 * Shorthand method. `$sce.trustAsHtml(value)` → 29766 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} 29767 * 29768 * @param {*} value The value to trustAs. 29769 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml 29770 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives 29771 * only accept expressions that are either literal constants or are the 29772 * return value of {@link ng.$sce#trustAs $sce.trustAs}.) 29773 */ 29774 29775 /** 29776 * @ngdoc method 29777 * @name $sce#trustAsUrl 29778 *
29779 * @description 29780 * Shorthand method. `$sce.trustAsUrl(value)` → 29781 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} 29782 * 29783 * @param {*} value The value to trustAs. 29784 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl 29785 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives 29786 * only accept expressions that are either literal constants or are the 29787 * return value of {@link ng.$sce#trustAs $sce.trustAs}.) 29788 */ 29789 29790 /** 29791 * @ngdoc method 29792 * @name $sce#trustAsResourceUrl 29793 * 29794 * @description 29795 * Shorthand method. `$sce.trustAsResourceUrl(value)` → 29796 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} 29797 * 29798 * @param {*} value The value to trustAs. 29799 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl 29800 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives 29801 * only accept expressions that are either literal constants or are the return 29802 * value of {@link ng.$sce#trustAs $sce.trustAs}.) 29803 */ 29804 29805 /** 29806 * @ngdoc method 29807 * @name $sce#trustAsJs 29808 * 29809 * @description 29810 * Shorthand method. `$sce.trustAsJs(value)` → 29811 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} 29812 * 29813 * @param {*} value The value to trustAs. 29814 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs 29815 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives 29816 * only accept expressions that are either literal constants or are the 29817 * return value of {@link ng.$sce#trustAs $sce.trustAs}.) 29818 */ 29819 29820 /** 29821 * @ngdoc method 29822 * @name $sce#getTrusted 29823 * 29824 * @description 29825 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, 29826 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the 29827 * originally supplied value if the queried context type is a supertype of the created type. 29828 * If this condition isn't satisfied, throws an exception. 29829 * 29830 * @param {string} type The kind of context in which this value is to be used. 29831 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} 29832 * call. 29833 * @returns {*} The value the was originally provided to 29834 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. 29835 * Otherwise, throws an exception. 29836 */ 29837 29838 /** 29839 * @ngdoc method 29840 * @name $sce#getTrustedHtml 29841 * 29842 * @description 29843 * Shorthand method. `$sce.getTrustedHtml(value)` → 29844 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} 29845 * 29846 * @param {*} value The value to pass to `$sce.getTrusted`. 29847 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` 29848 */ 29849 29850 /** 29851 * @ngdoc method 29852 * @name $sce#getTrustedCss 29853 * 29854 * @description 29855 * Shorthand method. `$sce.getTrustedCss(value)` → 29856 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} 29857 * 29858 * @param {*} value The value to pass to `$sce.getTrusted`. 29859 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` 29860 */ 29861 29862 /** 29863 * @ngdoc method 29864 * @name $sce#getTrustedUrl 29865 * 29866 * @description 29867 * Shorthand method. `$sce.getTrustedUrl(value)` → 29868 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} 29869 * 29870 * @param {*} value The value to pass to `$sce.getTrusted`. 29871 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` 29872 */ 29873 29874 /** 29875 * @ngdoc method 29876 * @name $sce#getTrustedResourceUrl 29877 * 29878 * @description 29879 * Shorthand method. `$sce.getTrustedResourceUrl(value)` → 29880 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} 29881 * 29882 * @param {*} value The value to pass to `$sceDelegate.getTrusted`. 29883 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` 29884 */ 29885 29886 /** 29887 * @ngdoc method 29888 * @name $sce#getTrustedJs 29889 *
vendor: 15,074 bytes, lines 29890-30280
29890 * @description 29891 * Shorthand method. `$sce.getTrustedJs(value)` → 29892 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} 29893 * 29894 * @param {*} value The value to pass to `$sce.getTrusted`. 29895 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` 29896 */ 29897 29898 /** 29899 * @ngdoc method 29900 * @name $sce#parseAsHtml 29901 * 29902 * @description 29903 * Shorthand method. `$sce.parseAsHtml(expression string)` → 29904 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} 29905 * 29906 * @param {string} expression String expression to compile. 29907 * @returns {function(context, locals)} a function which represents the compiled expression: 29908 * 29909 * * `context` – `{object}` – an object against which any expressions embedded in the strings 29910 * are evaluated against (typically a scope object). 29911 * * `locals` – `{object=}` – local variables context object, useful for overriding values in 29912 * `context`. 29913 */ 29914 29915 /** 29916 * @ngdoc method 29917 * @name $sce#parseAsCss 29918 * 29919 * @description 29920 * Shorthand method. `$sce.parseAsCss(value)` → 29921 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} 29922 * 29923 * @param {string} expression String expression to compile. 29924 * @returns {function(context, locals)} a function which represents the compiled expression: 29925 * 29926 * * `context` – `{object}` – an object against which any expressions embedded in the strings 29927 * are evaluated against (typically a scope object). 29928 * * `locals` – `{object=}` – local variables context object, useful for overriding values in 29929 * `context`. 29930 */ 29931 29932 /** 29933 * @ngdoc method 29934 * @name $sce#parseAsUrl 29935 * 29936 * @description 29937 * Shorthand method. `$sce.parseAsUrl(value)` → 29938 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} 29939 * 29940 * @param {string} expression String expression to compile. 29941 * @returns {function(context, locals)} a function which represents the compiled expression: 29942 * 29943 * * `context` – `{object}` – an object against which any expressions embedded in the strings 29944 * are evaluated against (typically a scope object). 29945 * * `locals` – `{object=}` – local variables context object, useful for overriding values in 29946 * `context`. 29947 */ 29948 29949 /** 29950 * @ngdoc method 29951 * @name $sce#parseAsResourceUrl 29952 * 29953 * @description 29954 * Shorthand method. `$sce.parseAsResourceUrl(value)` → 29955 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} 29956 * 29957 * @param {string} expression String expression to compile. 29958 * @returns {function(context, locals)} a function which represents the compiled expression: 29959 * 29960 * * `context` – `{object}` – an object against which any expressions embedded in the strings 29961 * are evaluated against (typically a scope object). 29962 * * `locals` – `{object=}` – local variables context object, useful for overriding values in 29963 * `context`. 29964 */ 29965 29966 /** 29967 * @ngdoc method 29968 * @name $sce#parseAsJs 29969 * 29970 * @description 29971 * Shorthand method. `$sce.parseAsJs(value)` → 29972 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} 29973 * 29974 * @param {string} expression String expression to compile. 29975 * @returns {function(context, locals)} a function which represents the compiled expression: 29976 * 29977 * * `context` – `{object}` – an object against which any expressions embedded in the strings 29978 * are evaluated against (typically a scope object). 29979 * * `locals` – `{object=}` – local variables context object, useful for overriding values in 29980 * `context`. 29981 */ 29982 29983 // Shorthand delegations. 29984 var parse = sce.parseAs, 29985 getTrusted = sce.getTrusted, 29986 trustAs = sce.trustAs; 29987 29988 forEach(SCE_CONTEXTS, function(enumValue, name) { 29989 var lName = lowercase(name); 29990 sce[snakeToCamel('parse_as_' + lName)] = function(expr) { 29991 return parse(enumValue, expr); 29992 }; 29993 sce[snakeToCamel('get_trusted_' + lName)] = function(value) { 29994 return getTrusted(enumValue, value); 29995 }; 29996 sce[snakeToCamel('trust_as_' + lName)] = function(value) { 29997 return trustAs(enumValue, value); 29998 }; 29999 }); 30000 30001 return sce; 30002 }]; 30003} 30004 30005/* exported $SnifferProvider */ 30006 30007/** 30008 * !!! This is an undocumented "private" service !!! 30009 * 30010 * @name $sniffer 30011 * @requires $window 30012 * @requires $document 30013 * @this 30014 * 30015 * @property {boolean} history Does the browser support html5 history api ? 30016 * @property {boolean} transitions Does the browser support CSS transition events ? 30017 * @property {boolean} animations Does the browser support CSS animation events ? 30018 * 30019 * @description 30020 * This is very simple implementation of testing browser's features. 30021 */ 30022function $SnifferProvider() { 30023 this.$get = ['$window', '$document', function($window, $document) { 30024 var eventSupport = {}, 30025 // Chrome Packaged Apps are not allowed to access `history.pushState`. 30026 // If not sandboxed, they can be detected by the presence of `chrome.app.runtime` 30027 // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by 30028 // the presence of an extension runtime ID and the absence of other Chrome runtime APIs 30029 // (see https://developer.chrome.com/apps/manifest/sandbox). 30030 // (NW.js apps have access to Chrome APIs, but do support `history`.) 30031 isNw = $window.nw && $window.nw.process, 30032 isChromePackagedApp = 30033 !isNw && 30034 $window.chrome && 30035 ($window.chrome.app && $window.chrome.app.runtime || 30036 !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id), 30037 hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState, 30038 android = 30039 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), 30040 boxee = /Boxee/i.test(($window.navigator || {}).userAgent), 30041 document = $document[0] || {}, 30042 bodyStyle = document.body && document.body.style, 30043 transitions = false, 30044 animations = false; 30045 30046 if (bodyStyle) { 30047 // Support: Android <5, Blackberry Browser 10, default Chrome in Android 4.4.x 30048 // Mentioned browsers need a -webkit- prefix for transitions & animations. 30049 transitions = !!('transition' in bodyStyle || 'webkitTransition' in bodyStyle); 30050 animations = !!('animation' in bodyStyle || 'webkitAnimation' in bodyStyle); 30051 } 30052 30053 30054 return { 30055 // Android has history.pushState, but it does not update location correctly 30056 // so let's not use the history API at all. 30057 // http://code.google.com/p/android/issues/detail?id=17471 30058 // https://github.com/angular/angular.js/issues/904 30059 30060 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has 30061 // so let's not use the history API also 30062 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined 30063 history: !!(hasHistoryPushState && !(android < 4) && !boxee), 30064 hasEvent: function(event) { 30065 // Support: IE 9-11 only 30066 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have 30067 // it. In particular the event is not fired when backspace or delete key are pressed or 30068 // when cut operation is performed. 30069 // IE10+ implements 'input' event but it erroneously fires under various situations, 30070 // e.g. when placeholder changes, or a form is focused. 30071 if (event === 'input' && msie) return false; 30072 30073 if (isUndefined(eventSupport[event])) { 30074 var divElm = document.createElement('div'); 30075 eventSupport[event] = 'on' + event in divElm; 30076 } 30077 30078 return eventSupport[event]; 30079 }, 30080 csp: csp(), 30081 transitions: transitions, 30082 animations: animations, 30083 android: android 30084 }; 30085 }]; 30086} 30087 30088var $templateRequestMinErr = minErr('$compile'); 30089 30090/** 30091 * @ngdoc provider 30092 * @name $templateRequestProvider 30093 * @this 30094 * 30095 * @description 30096 * Used to configure the options passed to the {@link $http} service when making a template request. 30097 * 30098 * For example, it can be used for specifying the "Accept" header that is sent to the server, when 30099 * requesting a template. 30100 */ 30101function $TemplateRequestProvider() { 30102 30103 var httpOptions; 30104 30105 /** 30106 * @ngdoc method 30107 * @name $templateRequestProvider#httpOptions 30108 * @description 30109 * The options to be passed to the {@link $http} service when making the request. 30110 * You can use this to override options such as the "Accept" header for template requests. 30111 * 30112 * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the 30113 * options if not overridden here. 30114 * 30115 * @param {string=} value new value for the {@link $http} options. 30116 * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter. 30117 */ 30118 this.httpOptions = function(val) { 30119 if (val) { 30120 httpOptions = val; 30121 return this; 30122 } 30123 return httpOptions; 30124 }; 30125 30126 /** 30127 * @ngdoc service 30128 * @name $templateRequest 30129 * 30130 * @description 30131 * The `$templateRequest` service runs security checks then downloads the provided template using 30132 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request 30133 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the 30134 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the 30135 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted 30136 * when `tpl` is of type string and `$templateCache` has the matching entry. 30137 * 30138 * If you want to pass custom options to the `$http` service, such as setting the Accept header you 30139 * can configure this via {@link $templateRequestProvider#httpOptions}. 30140 * 30141 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL 30142 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty 30143 * 30144 * @return {Promise} a promise for the HTTP response data of the given URL. 30145 * 30146 * @property {number} totalPendingRequests total amount of pending template requests being downloaded. 30147 */ 30148 this.$get = ['$exceptionHandler', '$templateCache', '$http', '$q', '$sce', 30149 function($exceptionHandler, $templateCache, $http, $q, $sce) { 30150 30151 function handleRequestFn(tpl, ignoreRequestError) { 30152 handleRequestFn.totalPendingRequests++; 30153 30154 // We consider the template cache holds only trusted templates, so 30155 // there's no need to go through whitelisting again for keys that already 30156 // are included in there. This also makes Angular accept any script 30157 // directive, no matter its name. However, we still need to unwrap trusted 30158 // types. 30159 if (!isString(tpl) || isUndefined($templateCache.get(tpl))) { 30160 tpl = $sce.getTrustedResourceUrl(tpl); 30161 } 30162 30163 var transformResponse = $http.defaults && $http.defaults.transformResponse; 30164 30165 if (isArray(transformResponse)) { 30166 transformResponse = transformResponse.filter(function(transformer) { 30167 return transformer !== defaultHttpResponseTransform; 30168 }); 30169 } else if (transformResponse === defaultHttpResponseTransform) { 30170 transformResponse = null; 30171 } 30172 30173 return $http.get(tpl, extend({ 30174 cache: $templateCache, 30175 transformResponse: transformResponse 30176 }, httpOptions)) 30177 .finally(function() { 30178 handleRequestFn.totalPendingRequests--; 30179 }) 30180 .then(function(response) { 30181 $templateCache.put(tpl, response.data); 30182 return response.data; 30183 }, handleError); 30184 30185 function handleError(resp) { 30186 if (!ignoreRequestError) { 30187 resp = $templateRequestMinErr('tpload', 30188 'Failed to load template: {0} (HTTP status: {1} {2})', 30189 tpl, resp.status, resp.statusText); 30190 30191 $exceptionHandler(resp); 30192 } 30193 30194 return $q.reject(resp); 30195 } 30196 } 30197 30198 handleRequestFn.totalPendingRequests = 0; 30199 30200 return handleRequestFn; 30201 } 30202 ]; 30203} 30204 30205/** @this */ 30206function $$TestabilityProvider() { 30207 this.$get = ['$rootScope', '$browser', '$location', 30208 function($rootScope, $browser, $location) { 30209 30210 /** 30211 * @name $testability 30212 * 30213 * @description 30214 * The private $$testability service provides a collection of methods for use when debugging 30215 * or by automated test and debugging tools. 30216 */ 30217 var testability = {}; 30218 30219 /** 30220 * @name $$testability#findBindings 30221 * 30222 * @description 30223 * Returns an array of elements that are bound (via ng-bind or {{}}) 30224 * to expressions matching the input. 30225 * 30226 * @param {Element} element The element root to search from. 30227 * @param {string} expression The binding expression to match. 30228 * @param {boolean} opt_exactMatch If true, only returns exact matches 30229 * for the expression. Filters and whitespace are ignored. 30230 */ 30231 testability.findBindings = function(element, expression, opt_exactMatch) { 30232 var bindings = element.getElementsByClassName('ng-binding'); 30233 var matches = []; 30234 forEach(bindings, function(binding) { 30235 var dataBinding = angular.element(binding).data('$binding'); 30236 if (dataBinding) { 30237 forEach(dataBinding, function(bindingName) { 30238 if (opt_exactMatch) { 30239 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)'); 30240 if (matcher.test(bindingName)) { 30241 matches.push(binding); 30242 } 30243 } else { 30244 if (bindingName.indexOf(expression) !== -1) { 30245 matches.push(binding); 30246 } 30247 } 30248 }); 30249 } 30250 }); 30251 return matches; 30252 }; 30253 30254 /** 30255 * @name $$testability#findModels 30256 * 30257 * @description 30258 * Returns an array of elements that are two-way found via ng-model to 30259 * expressions matching the input. 30260 * 30261 * @param {Element} element The element root to search from. 30262 * @param {string} expression The model expression to match. 30263 * @param {boolean} opt_exactMatch If true, only returns exact matches 30264 * for the expression. 30265 */ 30266 testability.findModels = function(element, expression, opt_exactMatch) { 30267 var prefixes = ['ng-', 'data-ng-', 'ng\\:']; 30268 for (var p = 0; p < prefixes.length; ++p) { 30269 var attributeEquals = opt_exactMatch ? '=' : '*='; 30270 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]'; 30271 var elements = element.querySelectorAll(selector); 30272 if (elements.length) { 30273 return elements; 30274 } 30275 } 30276 }; 30277 30278 /** 30279 * @name $$testability#getLocation 30280 *
30281 * @description 30282 * Shortcut for getting the location in a browser agnostic way. Returns 30283 * the path, search, and hash. (e.g. /path?a=b#hash) 30284 */ 30285 testability.getLocation = function() { 30286 return $location.url(); 30287 }; 30288 30289 /** 30290 * @name $$testability#setLocation 30291 * 30292 * @description 30293 * Shortcut for navigating to a location without doing a full page reload. 30294 * 30295 * @param {string} url The location url (path, search and hash, 30296 * e.g. /path?a=b#hash) to go to. 30297 */ 30298 testability.setLocation = function(url) { 30299 if (url !== $location.url()) { 30300 $location.url(url); 30301 $rootScope.$digest(); 30302 } 30303 }; 30304 30305 /** 30306 * @name $$testability#whenStable 30307 * 30308 * @description 30309 * Calls the callback when $timeout and $http requests are completed. 30310 * 30311 * @param {function} callback 30312 */ 30313 testability.whenStable = function(callback) { 30314 $browser.notifyWhenNoOutstandingRequests(callback); 30315 }; 30316 30317 return testability; 30318 }]; 30319} 30320 30321/** @this */ 30322function $TimeoutProvider() { 30323 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler', 30324 function($rootScope, $browser, $q, $$q, $exceptionHandler) { 30325 30326 var deferreds = {}; 30327 30328 30329 /** 30330 * @ngdoc service 30331 * @name $timeout 30332 * 30333 * @description 30334 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch 30335 * block and delegates any exceptions to 30336 * {@link ng.$exceptionHandler $exceptionHandler} service. 30337 * 30338 * The return value of calling `$timeout` is a promise, which will be resolved when 30339 * the delay has passed and the timeout function, if provided, is executed. 30340 * 30341 * To cancel a timeout request, call `$timeout.cancel(promise)`. 30342 * 30343 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to 30344 * synchronously flush the queue of deferred functions. 30345 * 30346 * If you only want a promise that will be resolved after some specified delay 30347 * then you can call `$timeout` without the `fn` function. 30348 * 30349 * @param {function()=} fn A function, whose execution should be delayed. 30350 * @param {number=} [delay=0] Delay in milliseconds. 30351 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise 30352 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. 30353 * @param {...*=} Pass additional parameters to the executed function. 30354 * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise 30355 * will be resolved with the return value of the `fn` function. 30356 * 30357 */ 30358 function timeout(fn, delay, invokeApply) { 30359 if (!isFunction(fn)) { 30360 invokeApply = delay; 30361 delay = fn; 30362 fn = noop; 30363 } 30364 30365 var args = sliceArgs(arguments, 3), 30366 skipApply = (isDefined(invokeApply) && !invokeApply), 30367 deferred = (skipApply ? $$q : $q).defer(), 30368 promise = deferred.promise, 30369 timeoutId; 30370 30371 timeoutId = $browser.defer(function() { 30372 try { 30373 deferred.resolve(fn.apply(null, args)); 30374 } catch (e) { 30375 deferred.reject(e); 30376 $exceptionHandler(e); 30377 } finally { 30378 delete deferreds[promise.$$timeoutId]; 30379 } 30380 30381 if (!skipApply) $rootScope.$apply(); 30382 }, delay); 30383 30384 promise.$$timeoutId = timeoutId; 30385 deferreds[timeoutId] = deferred; 30386 30387 return promise; 30388 } 30389 30390 30391 /** 30392 * @ngdoc method 30393 * @name $timeout#cancel 30394 * 30395 * @description 30396 * Cancels a task associated with the `promise`. As a result of this, the promise will be 30397 * resolved with a rejection. 30398 * 30399 * @param {Promise=} promise Promise returned by the `$timeout` function. 30400 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully 30401 * canceled. 30402 */ 30403 timeout.cancel = function(promise) { 30404 if (promise && promise.$$timeoutId in deferreds) { 30405 // Timeout cancels should not report an unhandled promise. 30406 deferreds[promise.$$timeoutId].promise.catch(noop); 30407 deferreds[promise.$$timeoutId].reject('canceled'); 30408 delete deferreds[promise.$$timeoutId]; 30409 return $browser.defer.cancel(promise.$$timeoutId); 30410 } 30411 return false;
vendor: 5,388 bytes, lines 30412-30535
30412 }; 30413 30414 return timeout; 30415 }]; 30416} 30417 30418// NOTE: The usage of window and document instead of $window and $document here is 30419// deliberate. This service depends on the specific behavior of anchor nodes created by the 30420// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and 30421// cause us to break tests. In addition, when the browser resolves a URL for XHR, it 30422// doesn't know about mocked locations and resolves URLs to the real document - which is 30423// exactly the behavior needed here. There is little value is mocking these out for this 30424// service. 30425var urlParsingNode = window.document.createElement('a'); 30426var originUrl = urlResolve(window.location.href); 30427 30428 30429/** 30430 * 30431 * Implementation Notes for non-IE browsers 30432 * ---------------------------------------- 30433 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, 30434 * results both in the normalizing and parsing of the URL. Normalizing means that a relative 30435 * URL will be resolved into an absolute URL in the context of the application document. 30436 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related 30437 * properties are all populated to reflect the normalized URL. This approach has wide 30438 * compatibility - Safari 1+, Mozilla 1+ etc. See 30439 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html 30440 * 30441 * Implementation Notes for IE 30442 * --------------------------- 30443 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other 30444 * browsers. However, the parsed components will not be set if the URL assigned did not specify 30445 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We 30446 * work around that by performing the parsing in a 2nd step by taking a previously normalized 30447 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the 30448 * properties such as protocol, hostname, port, etc. 30449 * 30450 * References: 30451 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement 30452 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html 30453 * http://url.spec.whatwg.org/#urlutils 30454 * https://github.com/angular/angular.js/pull/2902 30455 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ 30456 * 30457 * @kind function 30458 * @param {string} url The URL to be parsed. 30459 * @description Normalizes and parses a URL. 30460 * @returns {object} Returns the normalized URL as a dictionary. 30461 * 30462 * | member name | Description | 30463 * |---------------|----------------| 30464 * | href | A normalized version of the provided URL if it was not an absolute URL | 30465 * | protocol | The protocol including the trailing colon | 30466 * | host | The host and port (if the port is non-default) of the normalizedUrl | 30467 * | search | The search params, minus the question mark | 30468 * | hash | The hash string, minus the hash symbol 30469 * | hostname | The hostname 30470 * | port | The port, without ":" 30471 * | pathname | The pathname, beginning with "/" 30472 * 30473 */ 30474function urlResolve(url) { 30475 var href = url; 30476 30477 // Support: IE 9-11 only 30478 if (msie) { 30479 // Normalize before parse. Refer Implementation Notes on why this is 30480 // done in two steps on IE. 30481 urlParsingNode.setAttribute('href', href); 30482 href = urlParsingNode.href; 30483 } 30484 30485 urlParsingNode.setAttribute('href', href); 30486 30487 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils 30488 return { 30489 href: urlParsingNode.href, 30490 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', 30491 host: urlParsingNode.host, 30492 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', 30493 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', 30494 hostname: urlParsingNode.hostname, 30495 port: urlParsingNode.port, 30496 pathname: (urlParsingNode.pathname.charAt(0) === '/') 30497 ? urlParsingNode.pathname 30498 : '/' + urlParsingNode.pathname 30499 }; 30500} 30501 30502/** 30503 * Parse a request URL and determine whether this is a same-origin request as the application document. 30504 * 30505 * @param {string|object} requestUrl The url of the request as a string that will be resolved 30506 * or a parsed URL object. 30507 * @returns {boolean} Whether the request is for the same origin as the application document. 30508 */ 30509function urlIsSameOrigin(requestUrl) { 30510 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; 30511 return (parsed.protocol === originUrl.protocol && 30512 parsed.host === originUrl.host); 30513} 30514 30515/** 30516 * @ngdoc service 30517 * @name $window 30518 * @this 30519 * 30520 * @description 30521 * A reference to the browser's `window` object. While `window` 30522 * is globally available in JavaScript, it causes testability problems, because 30523 * it is a global variable. In angular we always refer to it through the 30524 * `$window` service, so it may be overridden, removed or mocked for testing. 30525 * 30526 * Expressions, like the one defined for the `ngClick` directive in the example 30527 * below, are evaluated with respect to the current scope. Therefore, there is 30528 * no risk of inadvertently coding in a dependency on a global value in such an 30529 * expression. 30530 * 30531 * @example 30532 <example module="windowExample" name="window-service"> 30533 <file name="index.html"> 30534 <script> 30535 angular.module('windowExample', [])
30536 .controller('ExampleController', ['$scope', '$window', function($scope, $window) { 30537 $scope.greeting = 'Hello, World!'; 30538 $scope.doGreeting = function(greeting) { 30539 $window.alert(greeting); 30540 }; 30541 }]); 30542 </script> 30543 <div ng-controller="ExampleController"> 30544 <input type="text" ng-model="greeting" aria-label="greeting" /> 30545 <button ng-click="doGreeting(greeting)">ALERT</button> 30546 </div> 30547 </file> 30548 <file name="protractor.js" type="protractor"> 30549 it('should display the greeting in the input box', function() { 30550 element(by.model('greeting')).sendKeys('Hello, E2E Tests'); 30551 // If we click the button it will block the test runner 30552 // element(':button').click(); 30553 }); 30554 </file> 30555 </example> 30556 */ 30557function $WindowProvider() { 30558 this.$get = valueFn(window); 30559} 30560 30561/** 30562 * @name $$cookieReader 30563 * @requires $document 30564 * 30565 * @description 30566 * This is a private service for reading cookies used by $http and ngCookies 30567 * 30568 * @return {Object} a key/value map of the current cookies 30569 */ 30570function $$CookieReader($document) { 30571 var rawDocument = $document[0] || {}; 30572 var lastCookies = {}; 30573 var lastCookieString = ''; 30574 30575 function safeGetCookie(rawDocument) { 30576 try { 30577 return rawDocument.cookie || ''; 30578 } catch (e) { 30579 return ''; 30580 } 30581 } 30582 30583 function safeDecodeURIComponent(str) { 30584 try { 30585 return decodeURIComponent(str); 30586 } catch (e) { 30587 return str; 30588 } 30589 } 30590 30591 return function() { 30592 var cookieArray, cookie, i, index, name; 30593 var currentCookieString = safeGetCookie(rawDocument); 30594 30595 if (currentCookieString !== lastCookieString) { 30596 lastCookieString = currentCookieString; 30597 cookieArray = lastCookieString.split('; '); 30598 lastCookies = {}; 30599 30600 for (i = 0; i < cookieArray.length; i++) { 30601 cookie = cookieArray[i]; 30602 index = cookie.indexOf('='); 30603 if (index > 0) { //ignore nameless cookies 30604 name = safeDecodeURIComponent(cookie.substring(0, index)); 30605 // the first value that is seen for a cookie is the most 30606 // specific one. values for the same cookie name that 30607 // follow are for less specific paths. 30608 if (isUndefined(lastCookies[name])) { 30609 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1)); 30610 } 30611 } 30612 } 30613 } 30614 return lastCookies; 30615 }; 30616} 30617 30618$$CookieReader.$inject = ['$document']; 30619 30620/** @this */ 30621function $$CookieReaderProvider() { 30622 this.$get = $$CookieReader; 30623} 30624 30625/* global currencyFilter: true, 30626 dateFilter: true, 30627 filterFilter: true, 30628 jsonFilter: true, 30629 limitToFilter: true, 30630 lowercaseFilter: true, 30631 numberFilter: true, 30632 orderByFilter: true, 30633 uppercaseFilter: true, 30634 */ 30635 30636/** 30637 * @ngdoc provider 30638 * @name $filterProvider 30639 * @description 30640 * 30641 * Filters are just functions which transform input to an output. However filters need to be 30642 * Dependency Injected. To achieve this a filter definition consists of a factory function which is 30643 * annotated with dependencies and is responsible for creating a filter function. 30644 * 30645 * <div class="alert alert-warning"> 30646 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. 30647 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace 30648 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores 30649 * (`myapp_subsection_filterx`). 30650 * </div> 30651 * 30652 * ```js 30653 * // Filter registration 30654 * function MyModule($provide, $filterProvider) { 30655 * // create a service to demonstrate injection (not always needed) 30656 * $provide.value('greet', function(name){ 30657 * return 'Hello ' + name + '!'; 30658 * }); 30659 * 30660 * // register a filter factory which uses the 30661 * // greet service to demonstrate DI. 30662 * $filterProvider.register('greet', function(greet){ 30663 * // return the filter function which uses the greet service 30664 * // to generate salutation 30665 * return function(text) { 30666 * // filters need to be forgiving so check input validity 30667 * return text && greet(text) || text; 30668 * }; 30669 * }); 30670 * } 30671 * ``` 30672 * 30673 * The filter function is registered with the `$injector` under the filter name suffix with 30674 * `Filter`. 30675 * 30676 * ```js 30677 * it('should be the same instance', inject( 30678 * function($filterProvider) { 30679 * $filterProvider.register('reverse', function(){ 30680 * return ...; 30681 * }); 30682 * }, 30683 * function($filter, reverseFilter) { 30684 * expect($filter('reverse')).toBe(reverseFilter); 30685 * }); 30686 * ``` 30687 * 30688 * 30689 * For more information about how angular filters work, and how to create your own filters, see 30690 * {@link guide/filter Filters} in the Angular Developer Guide. 30691 */ 30692 30693/** 30694 * @ngdoc service 30695 * @name $filter 30696 * @kind function 30697 * @description 30698 * Filters are used for formatting data displayed to the user. 30699 * 30700 * They can be used in view templates, controllers or services.Angular comes 30701 * with a collection of [built-in filters](api/ng/filter), but it is easy to 30702 * define your own as well. 30703 * 30704 * The general syntax in templates is as follows: 30705 * 30706 * ```html 30707 * {{ expression [| filter_name[:parameter_value] ... ] }} 30708 * ``` 30709 * 30710 * @param {String} name Name of the filter function to retrieve 30711 * @return {Function} the filter function 30712 * @example 30713 <example name="$filter" module="filterExample"> 30714 <file name="index.html"> 30715 <div ng-controller="MainCtrl"> 30716 <h3>{{ originalText }}</h3> 30717 <h3>{{ filteredText }}</h3> 30718 </div> 30719 </file> 30720 30721 <file name="script.js"> 30722 angular.module('filterExample', []) 30723 .controller('MainCtrl', function($scope, $filter) { 30724 $scope.originalText = 'hello'; 30725 $scope.filteredText = $filter('uppercase')($scope.originalText); 30726 }); 30727 </file> 30728 </example> 30729 */ 30730$FilterProvider.$inject = ['$provide']; 30731/** @this */ 30732function $FilterProvider($provide) { 30733 var suffix = 'Filter'; 30734 30735 /** 30736 * @ngdoc method 30737 * @name $filterProvider#register 30738 * @param {string|Object} name Name of the filter function, or an object map of filters where 30739 * the keys are the filter names and the values are the filter factories. 30740 * 30741 * <div class="alert alert-warning"> 30742 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. 30743 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace 30744 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores 30745 * (`myapp_subsection_filterx`). 30746 * </div> 30747 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered. 30748 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map 30749 * of the registered filter instances. 30750 */ 30751 function register(name, factory) { 30752 if (isObject(name)) { 30753 var filters = {};
30754 forEach(name, function(filter, key) { 30755 filters[key] = register(key, filter); 30756 }); 30757 return filters; 30758 } else { 30759 return $provide.factory(name + suffix, factory); 30760 } 30761 } 30762 this.register = register; 30763 30764 this.$get = ['$injector', function($injector) { 30765 return function(name) { 30766 return $injector.get(name + suffix); 30767 }; 30768 }]; 30769 30770 //////////////////////////////////////// 30771 30772 /* global 30773 currencyFilter: false, 30774 dateFilter: false, 30775 filterFilter: false, 30776 jsonFilter: false, 30777 limitToFilter: false, 30778 lowercaseFilter: false, 30779 numberFilter: false, 30780 orderByFilter: false, 30781 uppercaseFilter: false 30782 */ 30783 30784 register('currency', currencyFilter); 30785 register('date', dateFilter); 30786 register('filter', filterFilter); 30787 register('json', jsonFilter); 30788 register('limitTo', limitToFilter); 30789 register('lowercase', lowercaseFilter); 30790 register('number', numberFilter); 30791 register('orderBy', orderByFilter); 30792 register('uppercase', uppercaseFilter); 30793} 30794 30795/** 30796 * @ngdoc filter 30797 * @name filter 30798 * @kind function 30799 * 30800 * @description 30801 * Selects a subset of items from `array` and returns it as a new array. 30802 * 30803 * @param {Array} array The source array. 30804 * <div class="alert alert-info"> 30805 * **Note**: If the array contains objects that reference themselves, filtering is not possible. 30806 * </div> 30807 * @param {string|Object|function()} expression The predicate to be used for selecting items from 30808 * `array`. 30809 * 30810 * Can be one of: 30811 * 30812 * - `string`: The string is used for matching against the contents of the `array`. All strings or 30813 * objects with string properties in `array` that match this string will be returned. This also 30814 * applies to nested object properties. 30815 * The predicate can be negated by prefixing the string with `!`. 30816 * 30817 * - `Object`: A pattern object can be used to filter specific properties on objects contained 30818 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items 30819 * which have property `name` containing "M" and property `phone` containing "1". A special 30820 * property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match 30821 * against any property of the object or its nested object properties. That's equivalent to the 30822 * simple substring match with a `string` as described above. The special property name can be 30823 * overwritten, using the `anyPropertyKey` parameter. 30824 * The predicate can be negated by prefixing the string with `!`. 30825 * For example `{name: "!M"}` predicate will return an array of items which have property `name` 30826 * not containing "M". 30827 * 30828 * Note that a named property will match properties on the same level only, while the special 30829 * `$` property will match properties on the same level or deeper. E.g. an array item like 30830 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but 30831 * **will** be matched by `{$: 'John'}`. 30832 * 30833 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters. 30834 * The function is called for each element of the array, with the element, its index, and 30835 * the entire array itself as arguments. 30836 * 30837 * The final result is an array of those elements that the predicate returned true for. 30838 * 30839 * @param {function(actual, expected)|true|false} [comparator] Comparator which is used in 30840 * determining if the expected value (from the filter expression) and actual value (from 30841 * the object in the array) should be considered a match
vendor: 10,736 bytes, lines 30841-31129
30841. 30842 * 30843 * Can be one of: 30844 * 30845 * - `function(actual, expected)`: 30846 * The function will be given the object value and the predicate value to compare and 30847 * should return true if both values should be considered equal. 30848 * 30849 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. 30850 * This is essentially strict comparison of expected and actual. 30851 * 30852 * - `false`: A short hand for a function which will look for a substring match in a case 30853 * insensitive way. Primitive values are converted to strings. Objects are not compared against 30854 * primitives, unless they have a custom `toString` method (e.g. `Date` objects). 30855 * 30856 * 30857 * Defaults to `false`. 30858 * 30859 * @param {string} [anyPropertyKey] The special property name that matches against any property. 30860 * By default `$`. 30861 * 30862 * @example 30863 <example name="filter-filter"> 30864 <file name="index.html"> 30865 <div ng-init="friends = [{name:'John', phone:'555-1276'}, 30866 {name:'Mary', phone:'800-BIG-MARY'}, 30867 {name:'Mike', phone:'555-4321'}, 30868 {name:'Adam', phone:'555-5678'}, 30869 {name:'Julie', phone:'555-8765'}, 30870 {name:'Juliette', phone:'555-5678'}]"></div> 30871 30872 <label>Search: <input ng-model="searchText"></label> 30873 <table id="searchTextResults"> 30874 <tr><th>Name</th><th>Phone</th></tr> 30875 <tr ng-repeat="friend in friends | filter:searchText"> 30876 <td>{{friend.name}}</td> 30877 <td>{{friend.phone}}</td> 30878 </tr> 30879 </table> 30880 <hr> 30881 <label>Any: <input ng-model="search.$"></label> <br> 30882 <label>Name only <input ng-model="search.name"></label><br> 30883 <label>Phone only <input ng-model="search.phone"></label><br> 30884 <label>Equality <input type="checkbox" ng-model="strict"></label><br> 30885 <table id="searchObjResults"> 30886 <tr><th>Name</th><th>Phone</th></tr> 30887 <tr ng-repeat="friendObj in friends | filter:search:strict"> 30888 <td>{{friendObj.name}}</td> 30889 <td>{{friendObj.phone}}</td> 30890 </tr> 30891 </table> 30892 </file> 30893 <file name="protractor.js" type="protractor"> 30894 var expectFriendNames = function(expectedNames, key) { 30895 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { 30896 arr.forEach(function(wd, i) { 30897 expect(wd.getText()).toMatch(expectedNames[i]); 30898 }); 30899 }); 30900 }; 30901 30902 it('should search across all fields when filtering with a string', function() { 30903 var searchText = element(by.model('searchText')); 30904 searchText.clear(); 30905 searchText.sendKeys('m'); 30906 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); 30907 30908 searchText.clear(); 30909 searchText.sendKeys('76'); 30910 expectFriendNames(['John', 'Julie'], 'friend'); 30911 }); 30912 30913 it('should search in specific fields when filtering with a predicate object', function() { 30914 var searchAny = element(by.model('search.$')); 30915 searchAny.clear(); 30916 searchAny.sendKeys('i'); 30917 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); 30918 }); 30919 it('should use a equal comparison when comparator is true', function() { 30920 var searchName = element(by.model('search.name')); 30921 var strict = element(by.model('strict')); 30922 searchName.clear(); 30923 searchName.sendKeys('Julie'); 30924 strict.click(); 30925 expectFriendNames(['Julie'], 'friendObj'); 30926 }); 30927 </file> 30928 </example> 30929 */ 30930 30931function filterFilter() { 30932 return function(array, expression, comparator, anyPropertyKey) { 30933 if (!isArrayLike(array)) { 30934 if (array == null) { 30935 return array; 30936 } else { 30937 throw minErr('filter')('notarray', 'Expected array but received: {0}', array); 30938 } 30939 } 30940 30941 anyPropertyKey = anyPropertyKey || '$'; 30942 var expressionType = getTypeForFilter(expression); 30943 var predicateFn; 30944 var matchAgainstAnyProp; 30945 30946 switch (expressionType) { 30947 case 'function': 30948 predicateFn = expression; 30949 break; 30950 case 'boolean': 30951 case 'null': 30952 case 'number': 30953 case 'string': 30954 matchAgainstAnyProp = true; 30955 // falls through 30956 case 'object': 30957 predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp); 30958 break; 30959 default: 30960 return array; 30961 } 30962 30963 return Array.prototype.filter.call(array, predicateFn); 30964 }; 30965} 30966 30967// Helper functions for `filterFilter` 30968function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) { 30969 var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression); 30970 var predicateFn; 30971 30972 if (comparator === true) { 30973 comparator = equals; 30974 } else if (!isFunction(comparator)) { 30975 comparator = function(actual, expected) { 30976 if (isUndefined(actual)) { 30977 // No substring matching against `undefined` 30978 return false; 30979 } 30980 if ((actual === null) || (expected === null)) { 30981 // No substring matching against `null`; only match against `null` 30982 return actual === expected; 30983 } 30984 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) { 30985 // Should not compare primitives against objects, unless they have custom `toString` method 30986 return false; 30987 } 30988 30989 actual = lowercase('' + actual); 30990 expected = lowercase('' + expected); 30991 return actual.indexOf(expected) !== -1; 30992 }; 30993 } 30994 30995 predicateFn = function(item) { 30996 if (shouldMatchPrimitives && !isObject(item)) { 30997 return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false); 30998 } 30999 return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp); 31000 }; 31001 31002 return predicateFn; 31003} 31004 31005function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) { 31006 var actualType = getTypeForFilter(actual); 31007 var expectedType = getTypeForFilter(expected); 31008 31009 if ((expectedType === 'string') && (expected.charAt(0) === '!')) { 31010 return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp); 31011 } else if (isArray(actual)) { 31012 // In case `actual` is an array, consider it a match 31013 // if ANY of it's items matches `expected` 31014 return actual.some(function(item) { 31015 return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp); 31016 }); 31017 } 31018 31019 switch (actualType) { 31020 case 'object': 31021 var key; 31022 if (matchAgainstAnyProp) { 31023 for (key in actual) { 31024 // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined 31025 // See: https://github.com/angular/angular.js/issues/15644 31026 if (key.charAt && (key.charAt(0) !== '$') && 31027 deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) { 31028 return true; 31029 } 31030 } 31031 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false); 31032 } else if (expectedType === 'object') { 31033 for (key in expected) { 31034 var expectedVal = expected[key]; 31035 if (isFunction(expectedVal) || isUndefined(expectedVal)) { 31036 continue; 31037 } 31038 31039 var matchAnyProperty = key === anyPropertyKey; 31040 var actualVal = matchAnyProperty ? actual : actual[key]; 31041 if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) { 31042 return false; 31043 } 31044 } 31045 return true; 31046 } else { 31047 return comparator(actual, expected); 31048 } 31049 case 'function': 31050 return false; 31051 default: 31052 return comparator(actual, expected); 31053 } 31054} 31055 31056// Used for easily differentiating between `null` and actual `object` 31057function getTypeForFilter(val) { 31058 return (val === null) ? 'null' : typeof val; 31059} 31060 31061var MAX_DIGITS = 22; 31062var DECIMAL_SEP = '.'; 31063var ZERO_CHAR = '0'; 31064 31065/** 31066 * @ngdoc filter 31067 * @name currency 31068 * @kind function 31069 * 31070 * @description 31071 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default 31072 * symbol for current locale is used. 31073 * 31074 * @param {number} amount Input to filter. 31075 * @param {string=} symbol Currency symbol or identifier to be displayed. 31076 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale 31077 * @returns {string} Formatted number. 31078 * 31079 * 31080 * @example 31081 <example module="currencyExample" name="currency-filter"> 31082 <file name="index.html"> 31083 <script> 31084 angular.module('currencyExample', []) 31085 .controller('ExampleController', ['$scope', function($scope) { 31086 $scope.amount = 1234.56; 31087 }]); 31088 </script> 31089 <div ng-controller="ExampleController"> 31090 <input type="number" ng-model="amount" aria-label="amount"> <br> 31091 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br> 31092 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span><br> 31093 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span> 31094 </div> 31095 </file> 31096 <file name="protractor.js" type="protractor"> 31097 it('should init with 1234.56', function() { 31098 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); 31099 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56'); 31100 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235'); 31101 }); 31102 it('should update', function() { 31103 if (browser.params.browser === 'safari') { 31104 // Safari does not understand the minus key. See 31105 // https://github.com/angular/protractor/issues/481 31106 return; 31107 } 31108 element(by.model('amount')).clear(); 31109 element(by.model('amount')).sendKeys('-1234'); 31110 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00'); 31111 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00'); 31112 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234'); 31113 }); 31114 </file> 31115 </example> 31116 */ 31117currencyFilter.$inject = ['$locale']; 31118function currencyFilter($locale) { 31119 var formats = $locale.NUMBER_FORMATS; 31120 return function(amount, currencySymbol, fractionSize) { 31121 if (isUndefined(currencySymbol)) { 31122 currencySymbol = formats.CURRENCY_SYM; 31123 } 31124 31125 if (isUndefined(fractionSize)) { 31126 fractionSize = formats.PATTERNS[1].maxFrac; 31127 } 31128 31129 //
31129if null or undefined pass it through 31130 return (amount == null) 31131 ? amount 31132 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize). 31133 replace(/\u00A4/g
vendor: 13,487 bytes, lines 31133-31536
31133, currencySymbol); 31134 }; 31135} 31136 31137/** 31138 * @ngdoc filter 31139 * @name number 31140 * @kind function 31141 * 31142 * @description 31143 * Formats a number as text. 31144 * 31145 * If the input is null or undefined, it will just be returned. 31146 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively. 31147 * If the input is not a number an empty string is returned. 31148 * 31149 * 31150 * @param {number|string} number Number to format. 31151 * @param {(number|string)=} fractionSize Number of decimal places to round the number to. 31152 * If this is not provided then the fraction size is computed from the current locale's number 31153 * formatting pattern. In the case of the default locale, it will be 3. 31154 * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current 31155 * locale (e.g., in the en_US locale it will have "." as the decimal separator and 31156 * include "," group separators after each third digit). 31157 * 31158 * @example 31159 <example module="numberFilterExample" name="number-filter"> 31160 <file name="index.html"> 31161 <script> 31162 angular.module('numberFilterExample', []) 31163 .controller('ExampleController', ['$scope', function($scope) { 31164 $scope.val = 1234.56789; 31165 }]); 31166 </script> 31167 <div ng-controller="ExampleController"> 31168 <label>Enter number: <input ng-model='val'></label><br> 31169 Default formatting: <span id='number-default'>{{val | number}}</span><br> 31170 No fractions: <span>{{val | number:0}}</span><br> 31171 Negative number: <span>{{-val | number:4}}</span> 31172 </div> 31173 </file> 31174 <file name="protractor.js" type="protractor"> 31175 it('should format numbers', function() { 31176 expect(element(by.id('number-default')).getText()).toBe('1,234.568'); 31177 expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); 31178 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); 31179 }); 31180 31181 it('should update', function() { 31182 element(by.model('val')).clear(); 31183 element(by.model('val')).sendKeys('3374.333'); 31184 expect(element(by.id('number-default')).getText()).toBe('3,374.333'); 31185 expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); 31186 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); 31187 }); 31188 </file> 31189 </example> 31190 */ 31191numberFilter.$inject = ['$locale']; 31192function numberFilter($locale) { 31193 var formats = $locale.NUMBER_FORMATS; 31194 return function(number, fractionSize) { 31195 31196 // if null or undefined pass it through 31197 return (number == null) 31198 ? number 31199 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, 31200 fractionSize); 31201 }; 31202} 31203 31204/** 31205 * Parse a number (as a string) into three components that can be used 31206 * for formatting the number. 31207 * 31208 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/) 31209 * 31210 * @param {string} numStr The number to parse 31211 * @return {object} An object describing this number, containing the following keys: 31212 * - d : an array of digits containing leading zeros as necessary 31213 * - i : the number of the digits in `d` that are to the left of the decimal point 31214 * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d` 31215 * 31216 */ 31217function parse(numStr) { 31218 var exponent = 0, digits, numberOfIntegerDigits; 31219 var i, j, zeros; 31220 31221 // Decimal point? 31222 if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) { 31223 numStr = numStr.replace(DECIMAL_SEP, ''); 31224 } 31225 31226 // Exponential form? 31227 if ((i = numStr.search(/e/i)) > 0) { 31228 // Work out the exponent. 31229 if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i; 31230 numberOfIntegerDigits += +numStr.slice(i + 1); 31231 numStr = numStr.substring(0, i); 31232 } else if (numberOfIntegerDigits < 0) { 31233 // There was no decimal point or exponent so it is an integer. 31234 numberOfIntegerDigits = numStr.length; 31235 } 31236 31237 // Count the number of leading zeros. 31238 for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */ } 31239 31240 if (i === (zeros = numStr.length)) { 31241 // The digits are all zero. 31242 digits = [0]; 31243 numberOfIntegerDigits = 1; 31244 } else { 31245 // Count the number of trailing zeros 31246 zeros--; 31247 while (numStr.charAt(zeros) === ZERO_CHAR) zeros--; 31248 31249 // Trailing zeros are insignificant so ignore them 31250 numberOfIntegerDigits -= i; 31251 digits = []; 31252 // Convert string to array of digits without leading/trailing zeros. 31253 for (j = 0; i <= zeros; i++, j++) { 31254 digits[j] = +numStr.charAt(i); 31255 } 31256 } 31257 31258 // If the number overflows the maximum allowed digits then use an exponent. 31259 if (numberOfIntegerDigits > MAX_DIGITS) { 31260 digits = digits.splice(0, MAX_DIGITS - 1); 31261 exponent = numberOfIntegerDigits - 1; 31262 numberOfIntegerDigits = 1; 31263 } 31264 31265 return { d: digits, e: exponent, i: numberOfIntegerDigits }; 31266} 31267 31268/** 31269 * Round the parsed number to the specified number of decimal places 31270 * This function changed the parsedNumber in-place 31271 */ 31272function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) { 31273 var digits = parsedNumber.d; 31274 var fractionLen = digits.length - parsedNumber.i; 31275 31276 // determine fractionSize if it is not specified; `+fractionSize` converts it to a number 31277 fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize; 31278 31279 // The index of the digit to where rounding is to occur 31280 var roundAt = fractionSize + parsedNumber.i; 31281 var digit = digits[roundAt]; 31282 31283 if (roundAt > 0) { 31284 // Drop fractional digits beyond `roundAt` 31285 digits.splice(Math.max(parsedNumber.i, roundAt)); 31286 31287 // Set non-fractional digits beyond `roundAt` to 0 31288 for (var j = roundAt; j < digits.length; j++) { 31289 digits[j] = 0; 31290 } 31291 } else { 31292 // We rounded to zero so reset the parsedNumber 31293 fractionLen = Math.max(0, fractionLen); 31294 parsedNumber.i = 1; 31295 digits.length = Math.max(1, roundAt = fractionSize + 1); 31296 digits[0] = 0; 31297 for (var i = 1; i < roundAt; i++) digits[i] = 0; 31298 } 31299 31300 if (digit >= 5) { 31301 if (roundAt - 1 < 0) { 31302 for (var k = 0; k > roundAt; k--) { 31303 digits.unshift(0); 31304 parsedNumber.i++; 31305 } 31306 digits.unshift(1); 31307 parsedNumber.i++; 31308 } else { 31309 digits[roundAt - 1]++; 31310 } 31311 } 31312 31313 // Pad out with zeros to get the required fraction length 31314 for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0); 31315 31316 31317 // Do any carrying, e.g. a digit was rounded up to 10 31318 var carry = digits.reduceRight(function(carry, d, i, digits) { 31319 d = d + carry; 31320 digits[i] = d % 10; 31321 return Math.floor(d / 10); 31322 }, 0); 31323 if (carry) { 31324 digits.unshift(carry); 31325 parsedNumber.i++; 31326 } 31327} 31328 31329/** 31330 * Format a number into a string 31331 * @param {number} number The number to format 31332 * @param {{ 31333 * minFrac, // the minimum number of digits required in the fraction part of the number 31334 * maxFrac, // the maximum number of digits required in the fraction part of the number 31335 * gSize, // number of digits in each group of separated digits 31336 * lgSize, // number of digits in the last group of digits before the decimal separator 31337 * negPre, // the string to go in front of a negative number (e.g. `-` or `(`)) 31338 * posPre, // the string to go in front of a positive number 31339 * negSuf, // the string to go after a negative number (e.g. `)`) 31340 * posSuf // the string to go after a positive number 31341 * }} pattern 31342 * @param {string} groupSep The string to separate groups of number (e.g. `,`) 31343 * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`) 31344 * @param {[type]} fractionSize The size of the fractional part of the number 31345 * @return {string} The number formatted as a string 31346 */ 31347function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { 31348 31349 if (!(isString(number) || isNumber(number)) || isNaN(number)) return ''; 31350 31351 var isInfinity = !isFinite(number); 31352 var isZero = false; 31353 var numStr = Math.abs(number) + '', 31354 formattedText = '', 31355 parsedNumber; 31356 31357 if (isInfinity) { 31358 formattedText = '\u221e'; 31359 } else { 31360 parsedNumber = parse(numStr); 31361 31362 roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac); 31363 31364 var digits = parsedNumber.d; 31365 var integerLen = parsedNumber.i; 31366 var exponent = parsedNumber.e; 31367 var decimals = []; 31368 isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true); 31369 31370 // pad zeros for small numbers 31371 while (integerLen < 0) { 31372 digits.unshift(0); 31373 integerLen++; 31374 } 31375 31376 // extract decimals digits 31377 if (integerLen > 0) { 31378 decimals = digits.splice(integerLen, digits.length); 31379 } else { 31380 decimals = digits; 31381 digits = [0]; 31382 } 31383 31384 // format the integer digits with grouping separators 31385 var groups = []; 31386 if (digits.length >= pattern.lgSize) { 31387 groups.unshift(digits.splice(-pattern.lgSize, digits.length).join('')); 31388 } 31389 while (digits.length > pattern.gSize) { 31390 groups.unshift(digits.splice(-pattern.gSize, digits.length).join('')); 31391 } 31392 if (digits.length) { 31393 groups.unshift(digits.join('')); 31394 } 31395 formattedText = groups.join(groupSep); 31396 31397 // append the decimal digits 31398 if (decimals.length) { 31399 formattedText += decimalSep + decimals.join(''); 31400 } 31401 31402 if (exponent) { 31403 formattedText += 'e+' + exponent; 31404 } 31405 } 31406 if (number < 0 && !isZero) { 31407 return pattern.negPre + formattedText + pattern.negSuf; 31408 } else { 31409 return pattern.posPre + formattedText + pattern.posSuf; 31410 } 31411} 31412 31413function padNumber(num, digits, trim, negWrap) { 31414 var neg = ''; 31415 if (num < 0 || (negWrap && num <= 0)) { 31416 if (negWrap) { 31417 num = -num + 1; 31418 } else { 31419 num = -num; 31420 neg = '-'; 31421 } 31422 } 31423 num = '' + num; 31424 while (num.length < digits) num = ZERO_CHAR + num; 31425 if (trim) { 31426 num = num.substr(num.length - digits); 31427 } 31428 return neg + num; 31429} 31430 31431 31432function dateGetter(name, size, offset, trim, negWrap) { 31433 offset = offset || 0; 31434 return function(date) { 31435 var value = date['get' + name](); 31436 if (offset > 0 || value > -offset) { 31437 value += offset; 31438 } 31439 if (value === 0 && offset === -12) value = 12; 31440 return padNumber(value, size, trim, negWrap); 31441 }; 31442} 31443 31444function dateStrGetter(name, shortForm, standAlone) { 31445 return function(date, formats) { 31446 var value = date['get' + name](); 31447 var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : ''); 31448 var get = uppercase(propPrefix + name); 31449 31450 return formats[get][value]; 31451 }; 31452} 31453 31454function timeZoneGetter(date, formats, offset) { 31455 var zone = -1 * offset; 31456 var paddedZone = (zone >= 0) ? '+' : ''; 31457 31458 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + 31459 padNumber(Math.abs(zone % 60), 2); 31460 31461 return paddedZone; 31462} 31463 31464function getFirstThursdayOfYear(year) { 31465 // 0 = index of January 31466 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay(); 31467 // 4 = index of Thursday (+1 to account for 1st = 5) 31468 // 11 = index of *next* Thursday (+1 account for 1st = 12) 31469 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst); 31470} 31471 31472function getThursdayThisWeek(datetime) { 31473 return new Date(datetime.getFullYear(), datetime.getMonth(), 31474 // 4 = index of Thursday 31475 datetime.getDate() + (4 - datetime.getDay())); 31476} 31477 31478function weekGetter(size) { 31479 return function(date) { 31480 var firstThurs = getFirstThursdayOfYear(date.getFullYear()), 31481 thisThurs = getThursdayThisWeek(date); 31482 31483 var diff = +thisThurs - +firstThurs, 31484 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week 31485 31486 return padNumber(result, size); 31487 }; 31488} 31489 31490function ampmGetter(date, formats) { 31491 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; 31492} 31493 31494function eraGetter(date, formats) { 31495 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1]; 31496} 31497 31498function longEraGetter(date, formats) { 31499 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1]; 31500} 31501 31502var DATE_FORMATS = { 31503 yyyy: dateGetter('FullYear', 4, 0, false, true), 31504 yy: dateGetter('FullYear', 2, 0, true, true), 31505 y: dateGetter('FullYear', 1, 0, false, true), 31506 MMMM: dateStrGetter('Month'), 31507 MMM: dateStrGetter('Month', true), 31508 MM: dateGetter('Month', 2, 1), 31509 M: dateGetter('Month', 1, 1), 31510 LLLL: dateStrGetter('Month', false, true), 31511 dd: dateGetter('Date', 2), 31512 d: dateGetter('Date', 1), 31513 HH: dateGetter('Hours', 2), 31514 H: dateGetter('Hours', 1), 31515 hh: dateGetter('Hours', 2, -12), 31516 h: dateGetter('Hours', 1, -12), 31517 mm: dateGetter('Minutes', 2), 31518 m: dateGetter('Minutes', 1), 31519 ss: dateGetter('Seconds', 2), 31520 s: dateGetter('Seconds', 1), 31521 // while ISO 8601 requires fractions to be prefixed with `.` or `,` 31522 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions 31523 sss: dateGetter('Milliseconds', 3), 31524 EEEE: dateStrGetter('Day'), 31525 EEE: dateStrGetter('Day', true), 31526 a: ampmGetter, 31527 Z: timeZoneGetter, 31528 ww: weekGetter(2), 31529 w: weekGetter(1), 31530 G: eraGetter, 31531 GG: eraGetter, 31532 GGG: eraGetter, 31533 GGGG: longEraGetter 31534}; 31535 31536var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(
31536.*)/, 31537 NUMBER_STRING = /^-?\d+$/; 31538 31539/** 31540 * @ngdoc filter 31541 * @name date 31542 * @kind function 31543 * 31544 * @description 31545 * Formats `date` to a string based on the requested `format`. 31546 * 31547 * `format` string can be composed of the following elements: 31548 * 31549 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) 31550 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) 31551 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) 31552 * * `'MMMM'`: Month in year (January-December) 31553 * * `'MMM'`: Month in year (Jan-Dec) 31554 * * `'MM'`: Month in year, padded (01-12) 31555 * * `'M'`: Month in year (1-12) 31556 * * `'LLLL'`: Stand-alone month in year (January-December) 31557 * * `'dd'`: Day in month, padded (01-31) 31558 * * `'d'`: Day in month (1-31) 31559 * * `'EEEE'`: Day in Week,(Sunday-Saturday) 31560 * * `'EEE'`: Day in Week, (Sun-Sat) 31561 * * `'HH'`: Hour in day, padded (00-23) 31562 * * `'H'`: Hour in day (0-23) 31563 * * `'hh'`: Hour in AM/PM, padded (01-12) 31564 * * `'h'`: Hour in AM/PM, (1-12) 31565 * * `'mm'`: Minute in hour, padded (00-59) 31566 * * `'m'`: Minute in hour (0-59) 31567 * * `'ss'`: Second in minute, padded (00-59) 31568 * * `'s'`: Second in minute (0-59) 31569 * * `'sss'`: Millisecond in second, padded (000-999) 31570 * * `'a'`: AM/PM marker 31571 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) 31572 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year 31573 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year 31574 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD') 31575 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini') 31576 * 31577 * `format` string can also be one of the following predefined 31578 * {@link guide/i18n localizable formats}: 31579 * 31580 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale 31581 * (e.g. Sep 3, 2010 12:05:08 PM) 31582 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM) 31583 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale 31584 * (e.g. Friday, September 3, 2010) 31585 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) 31586 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) 31587 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) 31588 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM) 31589 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM) 31590 * 31591 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. 31592 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence 31593 * (e.g. `"h 'o''clock'"`)
vendor: 6,066 bytes, lines 31593-31757
31593. 31594 * 31595 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or 31596 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its 31597 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is 31598 * specified in the string input, the time is considered to be in the local timezone. 31599 * @param {string=} format Formatting rules (see Description). If not specified, 31600 * `mediumDate` is used. 31601 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the 31602 * continental US time zone abbreviations, but for general use, use a time zone offset, for 31603 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) 31604 * If not specified, the timezone of the browser will be used. 31605 * @returns {string} Formatted string or the input if input is not recognized as date/millis. 31606 * 31607 * @example 31608 <example name="filter-date"> 31609 <file name="index.html"> 31610 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>: 31611 <span>{{1288323623006 | date:'medium'}}</span><br> 31612 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>: 31613 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br> 31614 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>: 31615 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br> 31616 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>: 31617 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br> 31618 </file> 31619 <file name="protractor.js" type="protractor"> 31620 it('should format date', function() { 31621 expect(element(by.binding("1288323623006 | date:'medium'")).getText()). 31622 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); 31623 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). 31624 toMatch(/2010-10-2\d \d{2}:\d{2}:\d{2} (-|\+)?\d{4}/); 31625 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). 31626 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); 31627 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). 31628 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); 31629 }); 31630 </file> 31631 </example> 31632 */ 31633dateFilter.$inject = ['$locale']; 31634function dateFilter($locale) { 31635 31636 31637 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; 31638 // 1 2 3 4 5 6 7 8 9 10 11 31639 function jsonStringToDate(string) { 31640 var match; 31641 if ((match = string.match(R_ISO8601_STR))) { 31642 var date = new Date(0), 31643 tzHour = 0, 31644 tzMin = 0, 31645 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, 31646 timeSetter = match[8] ? date.setUTCHours : date.setHours; 31647 31648 if (match[9]) { 31649 tzHour = toInt(match[9] + match[10]); 31650 tzMin = toInt(match[9] + match[11]); 31651 } 31652 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3])); 31653 var h = toInt(match[4] || 0) - tzHour; 31654 var m = toInt(match[5] || 0) - tzMin; 31655 var s = toInt(match[6] || 0); 31656 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); 31657 timeSetter.call(date, h, m, s, ms); 31658 return date; 31659 } 31660 return string; 31661 } 31662 31663 31664 return function(date, format, timezone) { 31665 var text = '', 31666 parts = [], 31667 fn, match; 31668 31669 format = format || 'mediumDate'; 31670 format = $locale.DATETIME_FORMATS[format] || format; 31671 if (isString(date)) { 31672 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date); 31673 } 31674 31675 if (isNumber(date)) { 31676 date = new Date(date); 31677 } 31678 31679 if (!isDate(date) || !isFinite(date.getTime())) { 31680 return date; 31681 } 31682 31683 while (format) { 31684 match = DATE_FORMATS_SPLIT.exec(format); 31685 if (match) { 31686 parts = concat(parts, match, 1); 31687 format = parts.pop(); 31688 } else { 31689 parts.push(format); 31690 format = null; 31691 } 31692 } 31693 31694 var dateTimezoneOffset = date.getTimezoneOffset(); 31695 if (timezone) { 31696 dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); 31697 date = convertTimezoneToLocal(date, timezone, true); 31698 } 31699 forEach(parts, function(value) { 31700 fn = DATE_FORMATS[value]; 31701 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset) 31702 : value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\''); 31703 }); 31704 31705 return text; 31706 }; 31707} 31708 31709 31710/** 31711 * @ngdoc filter 31712 * @name json 31713 * @kind function 31714 * 31715 * @description 31716 * Allows you to convert a JavaScript object into JSON string. 31717 * 31718 * This filter is mostly useful for debugging. When using the double curly {{value}} notation 31719 * the binding is automatically converted to JSON. 31720 * 31721 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. 31722 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2. 31723 * @returns {string} JSON string. 31724 * 31725 * 31726 * @example 31727 <example name="filter-json"> 31728 <file name="index.html"> 31729 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre> 31730 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre> 31731 </file> 31732 <file name="protractor.js" type="protractor"> 31733 it('should jsonify filtered objects', function() { 31734 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n {2}"name": ?"value"\n}/); 31735 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n {4}"name": ?"value"\n}/); 31736 }); 31737 </file> 31738 </example> 31739 * 31740 */ 31741function jsonFilter() { 31742 return function(object, spacing) { 31743 if (isUndefined(spacing)) { 31744 spacing = 2; 31745 } 31746 return toJson(object, spacing); 31747 }; 31748} 31749 31750 31751/** 31752 * @ngdoc filter 31753 * @name lowercase 31754 * @kind function 31755 * @description 31756 * Converts string to lowercase. 31757 *
31757 @see angular.lowercase 31758 */ 31759var lowercaseFilter = valueFn(lowercase); 31760 31761 31762/** 31763 * @ngdoc filter 31764 * @name uppercase 31765 * @kind function 31766 * @description 31767 * Converts string to uppercase. 31768 * @see angular.uppercase
vendor: 7,356 bytes, lines 31768-31944
31768 31769 */ 31770var uppercaseFilter = valueFn(uppercase); 31771 31772/** 31773 * @ngdoc filter 31774 * @name limitTo 31775 * @kind function 31776 * 31777 * @description 31778 * Creates a new array or string containing only a specified number of elements. The elements are 31779 * taken from either the beginning or the end of the source array, string or number, as specified by 31780 * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported 31781 * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input, 31782 * it is converted to a string. 31783 * 31784 * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited. 31785 * @param {string|number} limit - The length of the returned array or string. If the `limit` number 31786 * is positive, `limit` number of items from the beginning of the source array/string are copied. 31787 * If the number is negative, `limit` number of items from the end of the source array/string 31788 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined, 31789 * the input will be returned unchanged. 31790 * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index, 31791 * `begin` indicates an offset from the end of `input`. Defaults to `0`. 31792 * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had 31793 * less than `limit` elements. 31794 * 31795 * @example 31796 <example module="limitToExample" name="limit-to-filter"> 31797 <file name="index.html"> 31798 <script> 31799 angular.module('limitToExample', []) 31800 .controller('ExampleController', ['$scope', function($scope) { 31801 $scope.numbers = [1,2,3,4,5,6,7,8,9]; 31802 $scope.letters = "abcdefghi"; 31803 $scope.longNumber = 2345432342; 31804 $scope.numLimit = 3; 31805 $scope.letterLimit = 3; 31806 $scope.longNumberLimit = 3; 31807 }]); 31808 </script> 31809 <div ng-controller="ExampleController"> 31810 <label> 31811 Limit {{numbers}} to: 31812 <input type="number" step="1" ng-model="numLimit"> 31813 </label> 31814 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p> 31815 <label> 31816 Limit {{letters}} to: 31817 <input type="number" step="1" ng-model="letterLimit"> 31818 </label> 31819 <p>Output letters: {{ letters | limitTo:letterLimit }}</p> 31820 <label> 31821 Limit {{longNumber}} to: 31822 <input type="number" step="1" ng-model="longNumberLimit"> 31823 </label> 31824 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p> 31825 </div> 31826 </file> 31827 <file name="protractor.js" type="protractor"> 31828 var numLimitInput = element(by.model('numLimit')); 31829 var letterLimitInput = element(by.model('letterLimit')); 31830 var longNumberLimitInput = element(by.model('longNumberLimit')); 31831 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); 31832 var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); 31833 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit')); 31834 31835 it('should limit the number array to first three items', function() { 31836 expect(numLimitInput.getAttribute('value')).toBe('3'); 31837 expect(letterLimitInput.getAttribute('value')).toBe('3'); 31838 expect(longNumberLimitInput.getAttribute('value')).toBe('3'); 31839 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); 31840 expect(limitedLetters.getText()).toEqual('Output letters: abc'); 31841 expect(limitedLongNumber.getText()).toEqual('Output long number: 234'); 31842 }); 31843 31844 // There is a bug in safari and protractor that doesn't like the minus key 31845 // it('should update the output when -3 is entered', function() { 31846 // numLimitInput.clear(); 31847 // numLimitInput.sendKeys('-3'); 31848 // letterLimitInput.clear(); 31849 // letterLimitInput.sendKeys('-3'); 31850 // longNumberLimitInput.clear(); 31851 // longNumberLimitInput.sendKeys('-3'); 31852 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); 31853 // expect(limitedLetters.getText()).toEqual('Output letters: ghi'); 31854 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342'); 31855 // }); 31856 31857 it('should not exceed the maximum size of input array', function() { 31858 numLimitInput.clear(); 31859 numLimitInput.sendKeys('100'); 31860 letterLimitInput.clear(); 31861 letterLimitInput.sendKeys('100'); 31862 longNumberLimitInput.clear(); 31863 longNumberLimitInput.sendKeys('100'); 31864 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); 31865 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); 31866 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); 31867 }); 31868 </file> 31869 </example> 31870*/ 31871function limitToFilter() { 31872 return function(input, limit, begin) { 31873 if (Math.abs(Number(limit)) === Infinity) { 31874 limit = Number(limit); 31875 } else { 31876 limit = toInt(limit); 31877 } 31878 if (isNumberNaN(limit)) return input; 31879 31880 if (isNumber(input)) input = input.toString(); 31881 if (!isArrayLike(input)) return input; 31882 31883 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin); 31884 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin; 31885 31886 if (limit >= 0) { 31887 return sliceFn(input, begin, begin + limit); 31888 } else { 31889 if (begin === 0) { 31890 return sliceFn(input, limit, input.length); 31891 } else { 31892 return sliceFn(input, Math.max(0, begin + limit), begin); 31893 } 31894 } 31895 }; 31896} 31897 31898function sliceFn(input, begin, end) { 31899 if (isString(input)) return input.slice(begin, end); 31900 31901 return slice.call(input, begin, end); 31902} 31903 31904/** 31905 * @ngdoc filter 31906 * @name orderBy 31907 * @kind function 31908 * 31909 * @description 31910 * Returns an array containing the items from the specified `collection`, ordered by a `comparator` 31911 * function based on the values computed using the `expression` predicate. 31912 * 31913 * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in 31914 * `[{id: 'bar'}, {id: 'foo'}]`. 31915 * 31916 * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray, 31917 * String, etc). 31918 * 31919 * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker 31920 * for the preceding one. The `expression` is evaluated against each item and the output is used 31921 * for comparing with other items. 31922 * 31923 * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in 31924 * ascending order. 31925 * 31926 * The comparison is done using the `comparator` function. If none is specified, a default, built-in 31927 * comparator is used (see below for details - in a nutshell, it compares numbers numerically and 31928 * strings alphabetically). 31929 * 31930 * ### Under the hood 31931 * 31932 * Ordering the specified `collection` happens in two phases: 31933 * 31934 * 1. All items are passed through the predicate (or predicates), and the returned values are saved 31935 * along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed 31936 * through a predicate that extracts the value of the `label` property, would be transformed to: 31937 * ``` 31938 * { 31939 * value: 'foo', 31940 * type: 'string', 31941 * index: ... 31942 * } 31943 * ``` 31944 *
319442. The comparator function is used to sort the items, based on the derived values, types and 31945 * indices. 31946 * 31947 * If you use a custom comparator, it will be called with pairs of objects of the form 31948 * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal 31949 * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the 31950 * second, or `1` otherwise. 31951 * 31952 * In order to ensure that the sorting will be deterministic across platforms, if none of the 31953 * specified predicates can distinguish between two items, `orderBy` will automatically introduce a 31954 * dummy predicate that returns the item's index as `value`. 31955 * (If you are using a custom comparator, make sure it can handle this predicate as well.) 31956 * 31957 * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted 31958 * value for an item, `orderBy` will try to convert that object to a primitive value, before passing 31959 * it to the comparator. The following rules govern the conversion: 31960 * 31961 * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be 31962 * used instead.<br /> 31963 * (If the object has a `valueOf()` method that returns another object, then the returned object 31964 * will be used in subsequent steps.) 31965 * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that 31966 * returns a primitive, its return value will be used instead.<br /> 31967 * (If the object has a `toString()` method that returns another object, then the returned object 31968 * will be used in subsequent steps.) 31969 * 3. No conversion; the object itself is used. 31970 * 31971 * ### The default comparator 31972 * 31973 * The default, built-in comparator should be sufficient for most usecases. In short, it compares 31974 * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to 31975 * using their index in the original collection, and sorts values of different types by type. 31976 * 31977 * More specifically, it follows these steps to determine the relative order of items: 31978 * 31979 * 1. If the compared values are of different types, compare the types themselves alphabetically. 31980 * 2. If both values are of type `string`, compare them alphabetically in a case- and 31981 * locale-insensitive way. 31982 * 3. If both values are objects, compare their indices instead. 31983 * 4. Otherwise, return: 31984 * - `0`, if the values are equal (by strict equality comparison, i.e. using `===`). 31985 * - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator). 31986 * - `1`, otherwise. 31987 * 31988 * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being 31989 * saved as numbers and not strings. 31990 * **Note:** For the purpose of sorting, `null` values are treated as the string `'null'` (i.e. 31991 * `type: 'string'`, `value: 'null'`). This may cause unexpected sort order relative to 31992 * other values. 31993 * 31994 * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort. 31995 * @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of 31996 * predicates) to be used by the comparator to determine the order of elements. 31997 * 31998 * Can be one of: 31999 * 32000 * - `Function`: A getter function. This function will be called with each item as argument and 32001 * the return value will be used for sorting. 32002 * - `string`: An Angular
vendor: 19,123 bytes, lines 32002-32503
32002 expression. This expression will be evaluated against each item and the 32003 * result will be used for sorting. For example, use `'label'` to sort by a property called 32004 * `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label` 32005 * property.<br /> 32006 * (The result of a constant expression is interpreted as a property name to be used for 32007 * comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a 32008 * property called `special name`.)<br /> 32009 * An expression can be optionally prefixed with `+` or `-` to control the sorting direction, 32010 * ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided, 32011 * (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons. 32012 * - `Array`: An array of function and/or string predicates. If a predicate cannot determine the 32013 * relative order of two items, the next predicate is used as a tie-breaker. 32014 * 32015 * **Note:** If the predicate is missing or empty then it defaults to `'+'`. 32016 * 32017 * @param {boolean=} reverse - If `true`, reverse the sorting order. 32018 * @param {(Function)=} comparator - The comparator function used to determine the relative order of 32019 * value pairs. If omitted, the built-in comparator will be used. 32020 * 32021 * @returns {Array} - The sorted array. 32022 * 32023 * 32024 * @example 32025 * ### Ordering a table with `ngRepeat` 32026 * 32027 * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by 32028 * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means 32029 * it defaults to the built-in comparator. 32030 * 32031 <example name="orderBy-static" module="orderByExample1"> 32032 <file name="index.html"> 32033 <div ng-controller="ExampleController"> 32034 <table class="friends"> 32035 <tr> 32036 <th>Name</th> 32037 <th>Phone Number</th> 32038 <th>Age</th> 32039 </tr> 32040 <tr ng-repeat="friend in friends | orderBy:'-age'"> 32041 <td>{{friend.name}}</td> 32042 <td>{{friend.phone}}</td> 32043 <td>{{friend.age}}</td> 32044 </tr> 32045 </table> 32046 </div> 32047 </file> 32048 <file name="script.js"> 32049 angular.module('orderByExample1', []) 32050 .controller('ExampleController', ['$scope', function($scope) { 32051 $scope.friends = [ 32052 {name: 'John', phone: '555-1212', age: 10}, 32053 {name: 'Mary', phone: '555-9876', age: 19}, 32054 {name: 'Mike', phone: '555-4321', age: 21}, 32055 {name: 'Adam', phone: '555-5678', age: 35}, 32056 {name: 'Julie', phone: '555-8765', age: 29} 32057 ]; 32058 }]); 32059 </file> 32060 <file name="style.css"> 32061 .friends { 32062 border-collapse: collapse; 32063 } 32064 32065 .friends th { 32066 border-bottom: 1px solid; 32067 } 32068 .friends td, .friends th { 32069 border-left: 1px solid; 32070 padding: 5px 10px; 32071 } 32072 .friends td:first-child, .friends th:first-child { 32073 border-left: none; 32074 } 32075 </file> 32076 <file name="protractor.js" type="protractor"> 32077 // Element locators 32078 var names = element.all(by.repeater('friends').column('friend.name')); 32079 32080 it('should sort friends by age in reverse order', function() { 32081 expect(names.get(0).getText()).toBe('Adam'); 32082 expect(names.get(1).getText()).toBe('Julie'); 32083 expect(names.get(2).getText()).toBe('Mike'); 32084 expect(names.get(3).getText()).toBe('Mary'); 32085 expect(names.get(4).getText()).toBe('John'); 32086 }); 32087 </file> 32088 </example> 32089 * <hr /> 32090 * 32091 * @example 32092 * ### Changing parameters dynamically 32093 * 32094 * All parameters can be changed dynamically. The next example shows how you can make the columns of 32095 * a table sortable, by binding the `expression` and `reverse` parameters to scope properties. 32096 * 32097 <example name="orderBy-dynamic" module="orderByExample2"> 32098 <file name="index.html"> 32099 <div ng-controller="ExampleController"> 32100 <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre> 32101 <hr/> 32102 <button ng-click="propertyName = null; reverse = false">Set to unsorted</button> 32103 <hr/> 32104 <table class="friends"> 32105 <tr> 32106 <th> 32107 <button ng-click="sortBy('name')">Name</button> 32108 <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span> 32109 </th> 32110 <th> 32111 <button ng-click="sortBy('phone')">Phone Number</button> 32112 <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span> 32113 </th> 32114 <th> 32115 <button ng-click="sortBy('age')">Age</button> 32116 <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span> 32117 </th> 32118 </tr> 32119 <tr ng-repeat="friend in friends | orderBy:propertyName:reverse"> 32120 <td>{{friend.name}}</td> 32121 <td>{{friend.phone}}</td> 32122 <td>{{friend.age}}</td> 32123 </tr> 32124 </table> 32125 </div> 32126 </file> 32127 <file name="script.js"> 32128 angular.module('orderByExample2', []) 32129 .controller('ExampleController', ['$scope', function($scope) { 32130 var friends = [ 32131 {name: 'John', phone: '555-1212', age: 10}, 32132 {name: 'Mary', phone: '555-9876', age: 19}, 32133 {name: 'Mike', phone: '555-4321', age: 21}, 32134 {name: 'Adam', phone: '555-5678', age: 35}, 32135 {name: 'Julie', phone: '555-8765', age: 29} 32136 ]; 32137 32138 $scope.propertyName = 'age'; 32139 $scope.reverse = true; 32140 $scope.friends = friends; 32141 32142 $scope.sortBy = function(propertyName) { 32143 $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false; 32144 $scope.propertyName = propertyName; 32145 }; 32146 }]); 32147 </file> 32148 <file name="style.css"> 32149 .friends { 32150 border-collapse: collapse; 32151 } 32152 32153 .friends th { 32154 border-bottom: 1px solid; 32155 } 32156 .friends td, .friends th { 32157 border-left: 1px solid; 32158 padding: 5px 10px; 32159 } 32160 .friends td:first-child, .friends th:first-child { 32161 border-left: none; 32162 } 32163 32164 .sortorder:after { 32165 content: '\25b2'; // BLACK UP-POINTING TRIANGLE 32166 } 32167 .sortorder.reverse:after { 32168 content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE 32169 } 32170 </file> 32171 <file name="protractor.js" type="protractor"> 32172 // Element locators 32173 var unsortButton = element(by.partialButtonText('unsorted')); 32174 var nameHeader = element(by.partialButtonText('Name')); 32175 var phoneHeader = element(by.partialButtonText('Phone')); 32176 var ageHeader = element(by.partialButtonText('Age')); 32177 var firstName = element(by.repeater('friends').column('friend.name').row(0)); 32178 var lastName = element(by.repeater('friends').column('friend.name').row(4)); 32179 32180 it('should sort friends by some property, when clicking on the column header', function() { 32181 expect(firstName.getText()).toBe('Adam'); 32182 expect(lastName.getText()).toBe('John'); 32183 32184 phoneHeader.click(); 32185 expect(firstName.getText()).toBe('John'); 32186 expect(lastName.getText()).toBe('Mary'); 32187 32188 nameHeader.click(); 32189 expect(firstName.getText()).toBe('Adam'); 32190 expect(lastName.getText()).toBe('Mike'); 32191 32192 ageHeader.click(); 32193 expect(firstName.getText()).toBe('John'); 32194 expect(lastName.getText()).toBe('Adam'); 32195 }); 32196 32197 it('should sort friends in reverse order, when clicking on the same column', function() { 32198 expect(firstName.getText()).toBe('Adam'); 32199 expect(lastName.getText()).toBe('John'); 32200 32201 ageHeader.click(); 32202 expect(firstName.getText()).toBe('John'); 32203 expect(lastName.getText()).toBe('Adam'); 32204 32205 ageHeader.click(); 32206 expect(firstName.getText()).toBe('Adam'); 32207 expect(lastName.getText()).toBe('John'); 32208 }); 32209 32210 it('should restore the original order, when clicking "Set to unsorted"', function() { 32211 expect(firstName.getText()).toBe('Adam'); 32212 expect(lastName.getText()).toBe('John'); 32213 32214 unsortButton.click(); 32215 expect(firstName.getText()).toBe('John'); 32216 expect(lastName.getText()).toBe('Julie'); 32217 }); 32218 </file> 32219 </example> 32220 * <hr /> 32221 * 32222 * @example 32223 * ### Using `orderBy` inside a controller 32224 * 32225 * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and 32226 * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory 32227 * and retrieve the `orderBy` filter with `$filter('orderBy')`.) 32228 * 32229 <example name="orderBy-call-manually" module="orderByExample3"> 32230 <file name="index.html"> 32231 <div ng-controller="ExampleController"> 32232 <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre> 32233 <hr/> 32234 <button ng-click="sortBy(null)">Set to unsorted</button> 32235 <hr/> 32236 <table class="friends"> 32237 <tr> 32238 <th> 32239 <button ng-click="sortBy('name')">Name</button> 32240 <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span> 32241 </th> 32242 <th> 32243 <button ng-click="sortBy('phone')">Phone Number</button> 32244 <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span> 32245 </th> 32246 <th> 32247 <button ng-click="sortBy('age')">Age</button> 32248 <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span> 32249 </th> 32250 </tr> 32251 <tr ng-repeat="friend in friends"> 32252 <td>{{friend.name}}</td> 32253 <td>{{friend.phone}}</td> 32254 <td>{{friend.age}}</td> 32255 </tr> 32256 </table> 32257 </div> 32258 </file> 32259 <file name="script.js"> 32260 angular.module('orderByExample3', []) 32261 .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) { 32262 var friends = [ 32263 {name: 'John', phone: '555-1212', age: 10}, 32264 {name: 'Mary', phone: '555-9876', age: 19}, 32265 {name: 'Mike', phone: '555-4321', age: 21}, 32266 {name: 'Adam', phone: '555-5678', age: 35}, 32267 {name: 'Julie', phone: '555-8765', age: 29} 32268 ]; 32269 32270 $scope.propertyName = 'age'; 32271 $scope.reverse = true; 32272 $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse); 32273 32274 $scope.sortBy = function(propertyName) { 32275 $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName) 32276 ? !$scope.reverse : false; 32277 $scope.propertyName = propertyName; 32278 $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse); 32279 }; 32280 }]); 32281 </file> 32282 <file name="style.css"> 32283 .friends { 32284 border-collapse: collapse; 32285 } 32286 32287 .friends th { 32288 border-bottom: 1px solid; 32289 } 32290 .friends td, .friends th { 32291 border-left: 1px solid; 32292 padding: 5px 10px; 32293 } 32294 .friends td:first-child, .friends th:first-child { 32295 border-left: none; 32296 } 32297 32298 .sortorder:after { 32299 content: '\25b2'; // BLACK UP-POINTING TRIANGLE 32300 } 32301 .sortorder.reverse:after { 32302 content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE 32303 } 32304 </file> 32305 <file name="protractor.js" type="protractor"> 32306 // Element locators 32307 var unsortButton = element(by.partialButtonText('unsorted')); 32308 var nameHeader = element(by.partialButtonText('Name')); 32309 var phoneHeader = element(by.partialButtonText('Phone')); 32310 var ageHeader = element(by.partialButtonText('Age')); 32311 var firstName = element(by.repeater('friends').column('friend.name').row(0)); 32312 var lastName = element(by.repeater('friends').column('friend.name').row(4)); 32313 32314 it('should sort friends by some property, when clicking on the column header', function() { 32315 expect(firstName.getText()).toBe('Adam'); 32316 expect(lastName.getText()).toBe('John'); 32317 32318 phoneHeader.click(); 32319 expect(firstName.getText()).toBe('John'); 32320 expect(lastName.getText()).toBe('Mary'); 32321 32322 nameHeader.click(); 32323 expect(firstName.getText()).toBe('Adam'); 32324 expect(lastName.getText()).toBe('Mike'); 32325 32326 ageHeader.click(); 32327 expect(firstName.getText()).toBe('John'); 32328 expect(lastName.getText()).toBe('Adam'); 32329 }); 32330 32331 it('should sort friends in reverse order, when clicking on the same column', function() { 32332 expect(firstName.getText()).toBe('Adam'); 32333 expect(lastName.getText()).toBe('John'); 32334 32335 ageHeader.click(); 32336 expect(firstName.getText()).toBe('John'); 32337 expect(lastName.getText()).toBe('Adam'); 32338 32339 ageHeader.click(); 32340 expect(firstName.getText()).toBe('Adam'); 32341 expect(lastName.getText()).toBe('John'); 32342 }); 32343 32344 it('should restore the original order, when clicking "Set to unsorted"', function() { 32345 expect(firstName.getText()).toBe('Adam'); 32346 expect(lastName.getText()).toBe('John'); 32347 32348 unsortButton.click(); 32349 expect(firstName.getText()).toBe('John'); 32350 expect(lastName.getText()).toBe('Julie'); 32351 }); 32352 </file> 32353 </example> 32354 * <hr /> 32355 * 32356 * @example 32357 * ### Using a custom comparator 32358 * 32359 * If you have very specific requirements about the way items are sorted, you can pass your own 32360 * comparator function. For example, you might need to compare some strings in a locale-sensitive 32361 * way. (When specifying a custom comparator, you also need to pass a value for the `reverse` 32362 * argument - passing `false` retains the default sorting order, i.e. ascending.) 32363 * 32364 <example name="orderBy-custom-comparator" module="orderByExample4"> 32365 <file name="index.html"> 32366 <div ng-controller="ExampleController"> 32367 <div class="friends-container custom-comparator"> 32368 <h3>Locale-sensitive Comparator</h3> 32369 <table class="friends"> 32370 <tr> 32371 <th>Name</th> 32372 <th>Favorite Letter</th> 32373 </tr> 32374 <tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator"> 32375 <td>{{friend.name}}</td> 32376 <td>{{friend.favoriteLetter}}</td> 32377 </tr> 32378 </table> 32379 </div> 32380 <div class="friends-container default-comparator"> 32381 <h3>Default Comparator</h3> 32382 <table class="friends"> 32383 <tr> 32384 <th>Name</th> 32385 <th>Favorite Letter</th> 32386 </tr> 32387 <tr ng-repeat="friend in friends | orderBy:'favoriteLetter'"> 32388 <td>{{friend.name}}</td> 32389 <td>{{friend.favoriteLetter}}</td> 32390 </tr> 32391 </table> 32392 </div> 32393 </div> 32394 </file> 32395 <file name="script.js"> 32396 angular.module('orderByExample4', []) 32397 .controller('ExampleController', ['$scope', function($scope) { 32398 $scope.friends = [ 32399 {name: 'John', favoriteLetter: 'Ä'}, 32400 {name: 'Mary', favoriteLetter: 'Ü'}, 32401 {name: 'Mike', favoriteLetter: 'Ö'}, 32402 {name: 'Adam', favoriteLetter: 'H'}, 32403 {name: 'Julie', favoriteLetter: 'Z'} 32404 ]; 32405 32406 $scope.localeSensitiveComparator = function(v1, v2) { 32407 // If we don't get strings, just compare by index 32408 if (v1.type !== 'string' || v2.type !== 'string') { 32409 return (v1.index < v2.index) ? -1 : 1; 32410 } 32411 32412 // Compare strings alphabetically, taking locale into account 32413 return v1.value.localeCompare(v2.value); 32414 }; 32415 }]); 32416 </file> 32417 <file name="style.css"> 32418 .friends-container { 32419 display: inline-block; 32420 margin: 0 30px; 32421 } 32422 32423 .friends { 32424 border-collapse: collapse; 32425 } 32426 32427 .friends th { 32428 border-bottom: 1px solid; 32429 } 32430 .friends td, .friends th { 32431 border-left: 1px solid; 32432 padding: 5px 10px; 32433 } 32434 .friends td:first-child, .friends th:first-child { 32435 border-left: none; 32436 } 32437 </file> 32438 <file name="protractor.js" type="protractor"> 32439 // Element locators 32440 var container = element(by.css('.custom-comparator')); 32441 var names = container.all(by.repeater('friends').column('friend.name')); 32442 32443 it('should sort friends by favorite letter (in correct alphabetical order)', function() { 32444 expect(names.get(0).getText()).toBe('John'); 32445 expect(names.get(1).getText()).toBe('Adam'); 32446 expect(names.get(2).getText()).toBe('Mike'); 32447 expect(names.get(3).getText()).toBe('Mary'); 32448 expect(names.get(4).getText()).toBe('Julie'); 32449 }); 32450 </file> 32451 </example> 32452 * 32453 */ 32454orderByFilter.$inject = ['$parse']; 32455function orderByFilter($parse) { 32456 return function(array, sortPredicate, reverseOrder, compareFn) { 32457 32458 if (array == null) return array; 32459 if (!isArrayLike(array)) { 32460 throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array); 32461 } 32462 32463 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; } 32464 if (sortPredicate.length === 0) { sortPredicate = ['+']; } 32465 32466 var predicates = processPredicates(sortPredicate); 32467 32468 var descending = reverseOrder ? -1 : 1; 32469 32470 // Define the `compare()` function. Use a default comparator if none is specified. 32471 var compare = isFunction(compareFn) ? compareFn : defaultCompare; 32472 32473 // The next three lines are a version of a Swartzian Transform idiom from Perl 32474 // (sometimes called the Decorate-Sort-Undecorate idiom) 32475 // See https://en.wikipedia.org/wiki/Schwartzian_transform 32476 var compareValues = Array.prototype.map.call(array, getComparisonObject); 32477 compareValues.sort(doComparison); 32478 array = compareValues.map(function(item) { return item.value; }); 32479 32480 return array; 32481 32482 function getComparisonObject(value, index) { 32483 // NOTE: We are adding an extra `tieBreaker` value based on the element's index. 32484 // This will be used to keep the sort stable when none of the input predicates can 32485 // distinguish between two elements. 32486 return { 32487 value: value, 32488 tieBreaker: {value: index, type: 'number', index: index}, 32489 predicateValues: predicates.map(function(predicate) { 32490 return getPredicateValue(predicate.get(value), index); 32491 }) 32492 }; 32493 } 32494 32495 function doComparison(v1, v2) { 32496 for (var i = 0, ii = predicates.length; i < ii; i++) { 32497 var result = compare(v1.predicateValues[i], v2.predicateValues[i]); 32498 if (result) { 32499 return result * predicates[i].descending * descending; 32500 } 32501 } 32502 32503 return
vendor: 5,453 bytes, lines 32503-32682
32503compare(v1.tieBreaker, v2.tieBreaker) * descending; 32504 } 32505 }; 32506 32507 function processPredicates(sortPredicates) { 32508 return sortPredicates.map(function(predicate) { 32509 var descending = 1, get = identity; 32510 32511 if (isFunction(predicate)) { 32512 get = predicate; 32513 } else if (isString(predicate)) { 32514 if ((predicate.charAt(0) === '+' || predicate.charAt(0) === '-')) { 32515 descending = predicate.charAt(0) === '-' ? -1 : 1; 32516 predicate = predicate.substring(1); 32517 } 32518 if (predicate !== '') { 32519 get = $parse(predicate); 32520 if (get.constant) { 32521 var key = get(); 32522 get = function(value) { return value[key]; }; 32523 } 32524 } 32525 } 32526 return {get: get, descending: descending}; 32527 }); 32528 } 32529 32530 function isPrimitive(value) { 32531 switch (typeof value) { 32532 case 'number': /* falls through */ 32533 case 'boolean': /* falls through */ 32534 case 'string': 32535 return true; 32536 default: 32537 return false; 32538 } 32539 } 32540 32541 function objectValue(value) { 32542 // If `valueOf` is a valid function use that 32543 if (isFunction(value.valueOf)) { 32544 value = value.valueOf(); 32545 if (isPrimitive(value)) return value; 32546 } 32547 // If `toString` is a valid function and not the one from `Object.prototype` use that 32548 if (hasCustomToString(value)) { 32549 value = value.toString(); 32550 if (isPrimitive(value)) return value; 32551 } 32552 32553 return value; 32554 } 32555 32556 function getPredicateValue(value, index) { 32557 var type = typeof value; 32558 if (value === null) { 32559 type = 'string'; 32560 value = 'null'; 32561 } else if (type === 'object') { 32562 value = objectValue(value); 32563 } 32564 return {value: value, type: type, index: index}; 32565 } 32566 32567 function defaultCompare(v1, v2) { 32568 var result = 0; 32569 var type1 = v1.type; 32570 var type2 = v2.type; 32571 32572 if (type1 === type2) { 32573 var value1 = v1.value; 32574 var value2 = v2.value; 32575 32576 if (type1 === 'string') { 32577 // Compare strings case-insensitively 32578 value1 = value1.toLowerCase(); 32579 value2 = value2.toLowerCase(); 32580 } else if (type1 === 'object') { 32581 // For basic objects, use the position of the object 32582 // in the collection instead of the value 32583 if (isObject(value1)) value1 = v1.index; 32584 if (isObject(value2)) value2 = v2.index; 32585 } 32586 32587 if (value1 !== value2) { 32588 result = value1 < value2 ? -1 : 1; 32589 } 32590 } else { 32591 result = type1 < type2 ? -1 : 1; 32592 } 32593 32594 return result; 32595 } 32596} 32597 32598function ngDirective(directive) { 32599 if (isFunction(directive)) { 32600 directive = { 32601 link: directive 32602 }; 32603 } 32604 directive.restrict = directive.restrict || 'AC'; 32605 return valueFn(directive); 32606} 32607 32608/** 32609 * @ngdoc directive 32610 * @name a 32611 * @restrict E 32612 * 32613 * @description 32614 * Modifies the default behavior of the html a tag so that the default action is prevented when 32615 * the href attribute is empty. 32616 * 32617 * For dynamically creating `href` attributes for a tags, see the {@link ng.ngHref `ngHref`} directive. 32618 */ 32619var htmlAnchorDirective = valueFn({ 32620 restrict: 'E', 32621 compile: function(element, attr) { 32622 if (!attr.href && !attr.xlinkHref) { 32623 return function(scope, element) { 32624 // If the linked element is not an anchor tag anymore, do nothing 32625 if (element[0].nodeName.toLowerCase() !== 'a') return; 32626 32627 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. 32628 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? 32629 'xlink:href' : 'href'; 32630 element.on('click', function(event) { 32631 // if we have no href url, then don't navigate anywhere. 32632 if (!element.attr(href)) { 32633 event.preventDefault(); 32634 } 32635 }); 32636 }; 32637 } 32638 } 32639}); 32640 32641/** 32642 * @ngdoc directive 32643 * @name ngHref 32644 * @restrict A 32645 * @priority 99 32646 * 32647 * @description 32648 * Using Angular markup like `{{hash}}` in an href attribute will 32649 * make the link go to the wrong URL if the user clicks it before 32650 * Angular has a chance to replace the `{{hash}}` markup with its 32651 * value. Until Angular replaces the markup the link will be broken 32652 * and will most likely return a 404 error. The `ngHref` directive 32653 * solves this problem. 32654 * 32655 * The wrong way to write it: 32656 * ```html 32657 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a> 32658 * ``` 32659 * 32660 * The correct way to write it: 32661 * ```html 32662 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a> 32663 * ``` 32664 * 32665 * @element A 32666 * @param {template} ngHref any string which can contain `{{}}` markup. 32667 * 32668 * @example 32669 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes 32670 * in links and their different behaviors: 32671 <example name="ng-href"> 32672 <file name="index.html"> 32673 <input ng-model="value" /><br /> 32674 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br /> 32675 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br /> 32676 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br /> 32677 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br /> 32678 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br /> 32679 <a id="link-6" ng-href="{{value}}">link</a> (link, change location) 32680 </file> 32681 <file name="protractor.js" type="protractor"> 32682 it('should execute ng-click but not reload when href without value', function() {
32683 element(by.id('link-1')).click(); 32684 expect(element(by.model('value')).getAttribute('value')).toEqual('1'); 32685 expect(element(by.id('link-1')).getAttribute('href')).toBe(''); 32686 }); 32687 32688 it('should execute ng-click but not reload when href empty string', function() { 32689 element(by.id('link-2')).click(); 32690 expect(element(by.model('value')).getAttribute('value')).toEqual('2'); 32691 expect(element(by.id('link-2')).getAttribute('href')).toBe(''); 32692 }); 32693 32694 it('should execute ng-click and change url when ng-href specified', function() { 32695 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); 32696 32697 element(by.id('link-3')).click(); 32698 32699 // At this point, we navigate away from an Angular page, so we need 32700 // to use browser.driver to get the base webdriver. 32701 32702 browser.wait(function() { 32703 return browser.driver.getCurrentUrl().then(function(url) { 32704 return url.match(/\/123$/); 32705 }); 32706 }, 5000, 'page should navigate to /123'); 32707 }); 32708 32709 it('should execute ng-click but not reload when href empty string and name specified', function() { 32710 element(by.id('link-4')).click(); 32711 expect(element(by.model('value')).getAttribute('value')).toEqual('4'); 32712 expect(element(by.id('link-4')).getAttribute('href')).toBe(''); 32713 }); 32714 32715 it('should execute ng-click but not reload when no href but name specified', function() { 32716 element(by.id('link-5')).click(); 32717 expect(element(by.model('value')).getAttribute('value')).toEqual('5'); 32718 expect(element(by.id('link-5')).getAttribute('href')).toBe(null); 32719 }); 32720 32721 it('should only change url when only ng-href', function() { 32722 element(by.model('value')).clear(); 32723 element(by.model('value')).sendKeys('6'); 32724 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); 32725 32726 element(by.id('link-6')).click(); 32727 32728 // At this point, we navigate away from an Angular page, so we need 32729 // to use browser.driver to get the base webdriver. 32730 browser.wait(function() { 32731 return browser.driver.getCurrentUrl().then(function(url) { 32732 return url.match(/\/6$/); 32733 }); 32734 }, 5000, 'page should navigate to /6'); 32735 }); 32736 </file> 32737 </example> 32738 */ 32739 32740/** 32741 * @ngdoc directive 32742 * @name ngSrc 32743 * @restrict A 32744 * @priority 99 32745 * 32746 * @description 32747 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't 32748 * work right: The browser will fetch from the URL with the literal 32749 * text `{{hash}}` until Angular replaces the expression inside 32750 * `{{hash}}`. The `ngSrc` directive solves this problem. 32751 * 32752 * The buggy way to write it: 32753 * ```html 32754 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/> 32755 * ``` 32756 * 32757 * The correct way to write it: 32758 * ```html 32759 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" /> 32760 * ``` 32761 * 32762 * @element IMG 32763 * @param {template} ngSrc any string which can contain `{{}}` markup. 32764 */ 32765 32766/** 32767 * @ngdoc directive 32768 * @name ngSrcset 32769 * @restrict A 32770 * @priority 99 32771 * 32772 * @description 32773 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't 32774 * work right: The browser will fetch from the URL with the literal 32775 * text `{{hash}}` until Angular replaces the expression inside 32776 * `{{hash}}`. The `ngSrcset` directive solves this problem. 32777 * 32778 * The buggy way to write it: 32779 * ```html 32780 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/> 32781 * ``` 32782 * 32783 * The correct way to write it: 32784 * ```html 32785 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" /> 32786 * ``` 32787 * 32788 * @element IMG 32789 * @param {template} ngSrcset any string which can contain `{{}}` markup. 32790 */ 32791 32792/** 32793 * @ngdoc directive 32794 * @name ngDisabled 32795 * @restrict A 32796 * @priority 100 32797 * 32798 * @description 32799 * 32800 * This directive sets the `disabled` attribute on the element (typically a form control, 32801 * e.g. `input`, `button`, `select` etc.) if the 32802 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. 32803 * 32804 * A special directive is necessary because we cannot use interpolation inside the `disabled` 32805 * attribute. See the {@link guide/interpolation interpolation guide} for more info. 32806 * 32807 * @example 32808 <example name="ng-disabled"> 32809 <file name="index.html"> 32810 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/> 32811 <button ng-model="button" ng-disabled="checked">Button</button> 32812 </file> 32813 <file name="protractor.js" type="protractor"> 32814 it('should toggle button', function() { 32815 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
32816 element(by.model('checked')).click(); 32817 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); 32818 }); 32819 </file> 32820 </example> 32821 * 32822 * @element INPUT 32823 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, 32824 * then the `disabled` attribute will be set on the element 32825 */ 32826 32827 32828/** 32829 * @ngdoc directive 32830 * @name ngChecked 32831 * @restrict A 32832 * @priority 100 32833 * 32834 * @description 32835 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy. 32836 * 32837 * Note that this directive should not be used together with {@link ngModel `ngModel`}, 32838 * as this can lead to unexpected behavior. 32839 * 32840 * A special directive is necessary because we cannot use interpolation inside the `checked` 32841 * attribute. See the {@link guide/interpolation interpolation guide} for more info. 32842 * 32843 * @example 32844 <example name="ng-checked"> 32845 <file name="index.html"> 32846 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/> 32847 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input"> 32848 </file> 32849 <file name="protractor.js" type="protractor"> 32850 it('should check both checkBoxes', function() { 32851 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy(); 32852 element(by.model('master')).click(); 32853 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); 32854 }); 32855 </file> 32856 </example> 32857 * 32858 * @element INPUT 32859 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, 32860 * then the `checked` attribute will be set on the element 32861 */ 32862 32863 32864/** 32865 * @ngdoc directive 32866 * @name ngReadonly 32867 * @restrict A 32868 * @priority 100 32869 * 32870 * @description 32871 * 32872 * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy. 32873 * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on 32874 * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information. 32875 * 32876 * A special directive is necessary because we cannot use interpolation inside the `readonly` 32877 * attribute. See the {@link guide/interpolation interpolation guide} for more info. 32878 * 32879 * @example 32880 <example name="ng-readonly"> 32881 <file name="index.html"> 32882 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/> 32883 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" /> 32884 </file> 32885 <file name="protractor.js" type="protractor"> 32886 it('should toggle readonly attr', function() { 32887 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); 32888 element(by.model('checked')).click(); 32889 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); 32890 }); 32891 </file> 32892 </example> 32893 * 32894 * @element INPUT 32895 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, 32896 * then special attribute "readonly" will be set on the element 32897 */ 32898 32899 32900/** 32901 * @ngdoc directive 32902 * @name ngSelected 32903 * @restrict A 32904 * @priority 100 32905 * 32906 * @description 32907 * 32908 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy. 32909 * 32910 * A special directive is necessary because we cannot use interpolation inside the `selected` 32911 * attribute. See the {@link guide/interpolation interpolation guide} for more info. 32912 * 32913 * <div class="alert alert-warning"> 32914 * **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only 32915 * sets the `selected` attribute on the element. If you are using `ngModel` on the select, you 32916 * should not use `ngSelected` on the options, as `ngModel` will set the select value and 32917 * selected options. 32918 * </div> 32919 * 32920 * @example 32921 <example name="ng-selected"> 32922 <file name="index.html"> 32923 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/> 32924 <select aria-label="ngSelected demo"> 32925 <option>Hello!</option> 32926 <option id="greet" ng-selected="selected">Greetings!</option> 32927 </select> 32928 </file> 32929 <file name="protractor.js" type="protractor"> 32930 it('should select Greetings!', function() { 32931 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
vendor: 8,388 bytes, lines 32932-33199
32932 element(by.model('selected')).click(); 32933 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); 32934 }); 32935 </file> 32936 </example> 32937 * 32938 * @element OPTION 32939 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, 32940 * then special attribute "selected" will be set on the element 32941 */ 32942 32943/** 32944 * @ngdoc directive 32945 * @name ngOpen 32946 * @restrict A 32947 * @priority 100 32948 * 32949 * @description 32950 * 32951 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy. 32952 * 32953 * A special directive is necessary because we cannot use interpolation inside the `open` 32954 * attribute. See the {@link guide/interpolation interpolation guide} for more info. 32955 * 32956 * ## A note about browser compatibility 32957 * 32958 * Edge, Firefox, and Internet Explorer do not support the `details` element, it is 32959 * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead. 32960 * 32961 * @example 32962 <example name="ng-open"> 32963 <file name="index.html"> 32964 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/> 32965 <details id="details" ng-open="open"> 32966 <summary>Show/Hide me</summary> 32967 </details> 32968 </file> 32969 <file name="protractor.js" type="protractor"> 32970 it('should toggle open', function() { 32971 expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); 32972 element(by.model('open')).click(); 32973 expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); 32974 }); 32975 </file> 32976 </example> 32977 * 32978 * @element DETAILS 32979 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, 32980 * then special attribute "open" will be set on the element 32981 */ 32982 32983var ngAttributeAliasDirectives = {}; 32984 32985// boolean attrs are evaluated 32986forEach(BOOLEAN_ATTR, function(propName, attrName) { 32987 // binding to multiple is not supported 32988 if (propName === 'multiple') return; 32989 32990 function defaultLinkFn(scope, element, attr) { 32991 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { 32992 attr.$set(attrName, !!value); 32993 }); 32994 } 32995 32996 var normalized = directiveNormalize('ng-' + attrName); 32997 var linkFn = defaultLinkFn; 32998 32999 if (propName === 'checked') { 33000 linkFn = function(scope, element, attr) { 33001 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input 33002 if (attr.ngModel !== attr[normalized]) { 33003 defaultLinkFn(scope, element, attr); 33004 } 33005 }; 33006 } 33007 33008 ngAttributeAliasDirectives[normalized] = function() { 33009 return { 33010 restrict: 'A', 33011 priority: 100, 33012 link: linkFn 33013 }; 33014 }; 33015}); 33016 33017// aliased input attrs are evaluated 33018forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) { 33019 ngAttributeAliasDirectives[ngAttr] = function() { 33020 return { 33021 priority: 100, 33022 link: function(scope, element, attr) { 33023 //special case ngPattern when a literal regular expression value 33024 //is used as the expression (this way we don't have to watch anything). 33025 if (ngAttr === 'ngPattern' && attr.ngPattern.charAt(0) === '/') { 33026 var match = attr.ngPattern.match(REGEX_STRING_REGEXP); 33027 if (match) { 33028 attr.$set('ngPattern', new RegExp(match[1], match[2])); 33029 return; 33030 } 33031 } 33032 33033 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) { 33034 attr.$set(ngAttr, value); 33035 }); 33036 } 33037 }; 33038 }; 33039}); 33040 33041// ng-src, ng-srcset, ng-href are interpolated 33042forEach(['src', 'srcset', 'href'], function(attrName) { 33043 var normalized = directiveNormalize('ng-' + attrName); 33044 ngAttributeAliasDirectives[normalized] = function() { 33045 return { 33046 priority: 99, // it needs to run after the attributes are interpolated 33047 link: function(scope, element, attr) { 33048 var propName = attrName, 33049 name = attrName; 33050 33051 if (attrName === 'href' && 33052 toString.call(element.prop('href')) === '[object SVGAnimatedString]') { 33053 name = 'xlinkHref'; 33054 attr.$attr[name] = 'xlink:href'; 33055 propName = null; 33056 } 33057 33058 attr.$observe(normalized, function(value) { 33059 if (!value) { 33060 if (attrName === 'href') { 33061 attr.$set(name, null); 33062 } 33063 return; 33064 } 33065 33066 attr.$set(name, value); 33067 33068 // Support: IE 9-11 only 33069 // On IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist 33070 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need 33071 // to set the property as well to achieve the desired effect. 33072 // We use attr[attrName] value since $set can sanitize the url. 33073 if (msie && propName) element.prop(propName, attr[name]); 33074 }); 33075 } 33076 }; 33077 }; 33078}); 33079 33080/* global -nullFormCtrl, -PENDING_CLASS, -SUBMITTED_CLASS 33081 */ 33082var nullFormCtrl = { 33083 $addControl: noop, 33084 $$renameControl: nullFormRenameControl, 33085 $removeControl: noop, 33086 $setValidity: noop, 33087 $setDirty: noop, 33088 $setPristine: noop, 33089 $setSubmitted: noop 33090}, 33091PENDING_CLASS = 'ng-pending', 33092SUBMITTED_CLASS = 'ng-submitted'; 33093 33094function nullFormRenameControl(control, name) { 33095 control.$name = name; 33096} 33097 33098/** 33099 * @ngdoc type 33100 * @name form.FormController 33101 * 33102 * @property {boolean} $pristine True if user has not interacted with the form yet. 33103 * @property {boolean} $dirty True if user has already interacted with the form. 33104 * @property {boolean} $valid True if all of the containing forms and controls are valid. 33105 * @property {boolean} $invalid True if at least one containing control or form is invalid. 33106 * @property {boolean} $pending True if at least one containing control or form is pending. 33107 * @property {boolean} $submitted True if user has submitted the form even if its invalid. 33108 * 33109 * @property {Object} $error Is an object hash, containing references to controls or 33110 * forms with failing validators, where: 33111 * 33112 * - keys are validation tokens (error names), 33113 * - values are arrays of controls or forms that have a failing validator for given error name. 33114 * 33115 * Built-in validation tokens: 33116 * 33117 * - `email` 33118 * - `max` 33119 * - `maxlength` 33120 * - `min` 33121 * - `minlength` 33122 * - `number` 33123 * - `pattern` 33124 * - `required` 33125 * - `url` 33126 * - `date` 33127 * - `datetimelocal` 33128 * - `time` 33129 * - `week` 33130 * - `month` 33131 * 33132 * @description 33133 * `FormController` keeps track of all its controls and nested forms as well as the state of them, 33134 * such as being valid/invalid or dirty/pristine. 33135 * 33136 * Each {@link ng.directive:form form} directive creates an instance 33137 * of `FormController`. 33138 * 33139 */ 33140//asks for $scope to fool the BC controller module 33141FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate']; 33142function FormController($element, $attrs, $scope, $animate, $interpolate) { 33143 this.$$controls = []; 33144 33145 // init state 33146 this.$error = {}; 33147 this.$$success = {}; 33148 this.$pending = undefined; 33149 this.$name = $interpolate($attrs.name || $attrs.ngForm || '')($scope); 33150 this.$dirty = false; 33151 this.$pristine = true; 33152 this.$valid = true; 33153 this.$invalid = false; 33154 this.$submitted = false; 33155 this.$$parentForm = nullFormCtrl; 33156 33157 this.$$element = $element; 33158 this.$$animate = $animate; 33159 33160 setupValidity(this); 33161} 33162 33163FormController.prototype = { 33164 /** 33165 * @ngdoc method 33166 * @name form.FormController#$rollbackViewValue 33167 * 33168 * @description 33169 * Rollback all form controls pending updates to the `$modelValue`. 33170 * 33171 * Updates may be pending by a debounced event or because the input is waiting for a some future 33172 * event defined in `ng-model-options`. This method is typically needed by the reset button of 33173 * a form that uses `ng-model-options` to pend updates. 33174 */ 33175 $rollbackViewValue: function() { 33176 forEach(this.$$controls, function(control) { 33177 control.$rollbackViewValue(); 33178 }); 33179 }, 33180 33181 /** 33182 * @ngdoc method 33183 * @name form.FormController#$commitViewValue 33184 * 33185 * @description 33186 * Commit all form controls pending updates to the `$modelValue`. 33187 * 33188 * Updates may be pending by a debounced event or because the input is waiting for a some future 33189 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController` 33190 * usually handles calling this in response to input events. 33191 */ 33192 $commitViewValue: function() { 33193 forEach(this.$$controls, function(control) { 33194 control.$commitViewValue(); 33195 }); 33196 }, 33197 33198 /** 33199 * @ngdoc method
33200 * @name form.FormController#$addControl 33201 * @param {object} control control object, either a {@link form.FormController} or an 33202 * {@link ngModel.NgModelController} 33203 * 33204 * @description 33205 * Register a control with the form. Input elements using ngModelController do this automatically 33206 * when they are linked. 33207 * 33208 * Note that the current state of the control will not be reflected on the new parent form. This 33209 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine` 33210 * state. 33211 * 33212 * However, if the method is used programmatically, for example by adding dynamically created controls, 33213 * or controls that have been previously removed without destroying their corresponding DOM element, 33214 * it's the developers responsibility to make sure the current state propagates to the parent form. 33215 * 33216 * For example, if an input control is added that is already `$dirty` and has `$error` properties, 33217 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form. 33218 */ 33219 $addControl: function(control) { 33220 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored 33221 // and not added to the scope. Now we throw an error. 33222 assertNotHasOwnProperty(control.$name, 'input'); 33223 this.$$controls.push(control); 33224 33225 if (control.$name) { 33226 this[control.$name] = control; 33227 } 33228 33229 control.$$parentForm = this; 33230 }, 33231 33232 // Private API: rename a form control 33233 $$renameControl: function(control, newName) { 33234 var oldName = control.$name; 33235 33236 if (this[oldName] === control) { 33237 delete this[oldName]; 33238 } 33239 this[newName] = control; 33240 control.$name = newName; 33241 }, 33242 33243 /** 33244 * @ngdoc method 33245 * @name form.FormController#$removeControl 33246 * @param {object} control control object, either a {@link form.FormController} or an 33247 * {@link ngModel.NgModelController} 33248 * 33249 * @description 33250 * Deregister a control from the form. 33251 * 33252 * Input elements using ngModelController do this automatically when they are destroyed. 33253 * 33254 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the 33255 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be 33256 * different from case to case. For example, removing the only `$dirty` control from a form may or 33257 * may not mean that the form is still `$dirty`. 33258 */ 33259 $removeControl: function(control) { 33260 if (control.$name && this[control.$name] === control) { 33261 delete this[control.$name]; 33262 } 33263 forEach(this.$pending, function(value, name) { 33264 // eslint-disable-next-line no-invalid-this 33265 this.$setValidity(name, null, control); 33266 }, this); 33267 forEach(this.$error, function(value, name) { 33268 // eslint-disable-next-line no-invalid-this 33269 this.$setValidity(name, null, control); 33270 }, this); 33271 forEach(this.$$success, function(value, name) { 33272 // eslint-disable-next-line no-invalid-this 33273 this.$setValidity(name, null, control); 33274 }, this); 33275 33276 arrayRemove(this.$$controls, control); 33277 control.$$parentForm = nullFormCtrl; 33278 }, 33279 33280 /** 33281 * @ngdoc method 33282 * @name form.FormController#$setDirty 33283 * 33284 * @description 33285 * Sets the form to a dirty state. 33286 * 33287 * This method can be called to add the 'ng-dirty' class and set the form to a dirty 33288 * state (ng-dirty class). This method will also propagate to parent forms. 33289 */ 33290 $setDirty: function() { 33291 this.$$animate.removeClass(this.$$element, PRISTINE_CLASS); 33292 this.$$animate.addClass(this.$$element, DIRTY_CLASS); 33293 this.$dirty = true; 33294 this.$pristine = false; 33295 this.$$parentForm.$setDirty(); 33296 }, 33297 33298 /** 33299 * @ngdoc method 33300 * @name form.FormController#$setPristine 33301 * 33302 * @description 33303 * Sets the form to its pristine state. 33304 * 33305 * This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes 33306 * the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted` 33307 * state to false. 33308 * 33309 * This method will also propagate to all the controls contained in this form. 33310 * 33311 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after 33312 * saving or resetting it. 33313 */ 33314 $setPristine: function() { 33315 this.$$animate.setClass(this.$$element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); 33316 this.$dirty = false;
vendor: 2,038 bytes, lines 33317-33400
33317 this.$pristine = true; 33318 this.$submitted = false; 33319 forEach(this.$$controls, function(control) { 33320 control.$setPristine(); 33321 }); 33322 }, 33323 33324 /** 33325 * @ngdoc method 33326 * @name form.FormController#$setUntouched 33327 * 33328 * @description 33329 * Sets the form to its untouched state. 33330 * 33331 * This method can be called to remove the 'ng-touched' class and set the form controls to their 33332 * untouched state (ng-untouched class). 33333 * 33334 * Setting a form controls back to their untouched state is often useful when setting the form 33335 * back to its pristine state. 33336 */ 33337 $setUntouched: function() { 33338 forEach(this.$$controls, function(control) { 33339 control.$setUntouched(); 33340 }); 33341 }, 33342 33343 /** 33344 * @ngdoc method 33345 * @name form.FormController#$setSubmitted 33346 * 33347 * @description 33348 * Sets the form to its submitted state. 33349 */ 33350 $setSubmitted: function() { 33351 this.$$animate.addClass(this.$$element, SUBMITTED_CLASS); 33352 this.$submitted = true; 33353 this.$$parentForm.$setSubmitted(); 33354 } 33355}; 33356 33357/** 33358 * @ngdoc method 33359 * @name form.FormController#$setValidity 33360 * 33361 * @description 33362 * Sets the validity of a form control. 33363 * 33364 * This method will also propagate to parent forms. 33365 */ 33366addSetValidityMethod({ 33367 clazz: FormController, 33368 set: function(object, property, controller) { 33369 var list = object[property]; 33370 if (!list) { 33371 object[property] = [controller]; 33372 } else { 33373 var index = list.indexOf(controller); 33374 if (index === -1) { 33375 list.push(controller); 33376 } 33377 } 33378 }, 33379 unset: function(object, property, controller) { 33380 var list = object[property]; 33381 if (!list) { 33382 return; 33383 } 33384 arrayRemove(list, controller); 33385 if (list.length === 0) { 33386 delete object[property]; 33387 } 33388 } 33389}); 33390 33391/** 33392 * @ngdoc directive 33393 * @name ngForm 33394 * @restrict EAC 33395 * 33396 * @description 33397 * Nestable alias of {@link ng.directive:form `form`} directive. HTML 33398 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a 33399 * sub-group of controls needs to be determined. 33400 *
33401 * Note: the purpose of `ngForm` is to group controls, 33402 * but not to be a replacement for the `<form>` tag with all of its capabilities 33403 * (e.g. posting to the server, ...). 33404 * 33405 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into 33406 * related scope, under this name. 33407 * 33408 */ 33409 33410 /** 33411 * @ngdoc directive 33412 * @name form 33413 * @restrict E 33414 * 33415 * @description 33416 * Directive that instantiates 33417 * {@link form.FormController FormController}. 33418 * 33419 * If the `name` attribute is specified, the form controller is published onto the current scope under 33420 * this name. 33421 * 33422 * # Alias: {@link ng.directive:ngForm `ngForm`} 33423 * 33424 * In Angular, forms can be nested. This means that the outer form is valid when all of the child 33425 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so 33426 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to 33427 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group 33428 * of controls needs to be determined. 33429 * 33430 * # CSS classes 33431 * - `ng-valid` is set if the form is valid. 33432 * - `ng-invalid` is set if the form is invalid. 33433 * - `ng-pending` is set if the form is pending. 33434 * - `ng-pristine` is set if the form is pristine. 33435 * - `ng-dirty` is set if the form is dirty. 33436 * - `ng-submitted` is set if the form was submitted. 33437 * 33438 * Keep in mind that ngAnimate can detect each of these classes when added and removed. 33439 * 33440 * 33441 * # Submitting a form and preventing the default action 33442 * 33443 * Since the role of forms in client-side Angular applications is different than in classical 33444 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full 33445 * page reload that sends the data to the server. Instead some javascript logic should be triggered 33446 * to handle the form submission in an application-specific way. 33447 * 33448 * For this reason, Angular prevents the default action (form submission to the server) unless the 33449 * `<form>` element has an `action` attribute specified. 33450 * 33451 * You can use one of the following two ways to specify what javascript method should be called when 33452 * a form is submitted: 33453 * 33454 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element 33455 * - {@link ng.directive:ngClick ngClick} directive on the first 33456 * button or input field of type submit (input[type=submit]) 33457 * 33458 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} 33459 * or {@link ng.directive:ngClick ngClick} directives. 33460 * This is because of the following form submission rules in the HTML specification: 33461 * 33462 * - If a form has only one input field then hitting enter in this field triggers form submit 33463 * (`ngSubmit`) 33464 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter 33465 * doesn't trigger submit 33466 * - if a form has one or more input fields and one or more buttons or input[type=submit] then 33467 * hitting enter in any of the input fields will trigger the click handler on the *first* button or 33468 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) 33469 * 33470 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is 33471 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` 33472 * to have access to the updated model. 33473 * 33474 * ## Animation Hooks 33475 *
vendor: 11,664 bytes, lines 33475-33787
33475 33476 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. 33477 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any 33478 * other validations that are performed within the form. Animations in ngForm are similar to how 33479 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well 33480 * as JS animations. 33481 * 33482 * The following example shows a simple way to utilize CSS transitions to style a form element 33483 * that has been rendered as invalid after it has been validated: 33484 * 33485 * <pre> 33486 * //be sure to include ngAnimate as a module to hook into more 33487 * //advanced animations 33488 * .my-form { 33489 * transition:0.5s linear all; 33490 * background: white; 33491 * } 33492 * .my-form.ng-invalid { 33493 * background: red; 33494 * color:white; 33495 * } 33496 * </pre> 33497 * 33498 * @example 33499 <example name="ng-form" deps="angular-animate.js" animations="true" fixBase="true" module="formExample"> 33500 <file name="index.html"> 33501 <script> 33502 angular.module('formExample', []) 33503 .controller('FormController', ['$scope', function($scope) { 33504 $scope.userType = 'guest'; 33505 }]); 33506 </script> 33507 <style> 33508 .my-form { 33509 transition:all linear 0.5s; 33510 background: transparent; 33511 } 33512 .my-form.ng-invalid { 33513 background: red; 33514 } 33515 </style> 33516 <form name="myForm" ng-controller="FormController" class="my-form"> 33517 userType: <input name="input" ng-model="userType" required> 33518 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br> 33519 <code>userType = {{userType}}</code><br> 33520 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br> 33521 <code>myForm.input.$error = {{myForm.input.$error}}</code><br> 33522 <code>myForm.$valid = {{myForm.$valid}}</code><br> 33523 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br> 33524 </form> 33525 </file> 33526 <file name="protractor.js" type="protractor"> 33527 it('should initialize to model', function() { 33528 var userType = element(by.binding('userType')); 33529 var valid = element(by.binding('myForm.input.$valid')); 33530 33531 expect(userType.getText()).toContain('guest'); 33532 expect(valid.getText()).toContain('true'); 33533 }); 33534 33535 it('should be invalid if empty', function() { 33536 var userType = element(by.binding('userType')); 33537 var valid = element(by.binding('myForm.input.$valid')); 33538 var userInput = element(by.model('userType')); 33539 33540 userInput.clear(); 33541 userInput.sendKeys(''); 33542 33543 expect(userType.getText()).toEqual('userType ='); 33544 expect(valid.getText()).toContain('false'); 33545 }); 33546 </file> 33547 </example> 33548 * 33549 * @param {string=} name Name of the form. If specified, the form controller will be published into 33550 * related scope, under this name. 33551 */ 33552var formDirectiveFactory = function(isNgForm) { 33553 return ['$timeout', '$parse', function($timeout, $parse) { 33554 var formDirective = { 33555 name: 'form', 33556 restrict: isNgForm ? 'EAC' : 'E', 33557 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form 33558 controller: FormController, 33559 compile: function ngFormCompile(formElement, attr) { 33560 // Setup initial state of the control 33561 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS); 33562 33563 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false); 33564 33565 return { 33566 pre: function ngFormPreLink(scope, formElement, attr, ctrls) { 33567 var controller = ctrls[0]; 33568 33569 // if `action` attr is not present on the form, prevent the default action (submission) 33570 if (!('action' in attr)) { 33571 // we can't use jq events because if a form is destroyed during submission the default 33572 // action is not prevented. see #1238 33573 // 33574 // IE 9 is not affected because it doesn't fire a submit event and try to do a full 33575 // page reload if the form was destroyed by submission of the form via a click handler 33576 // on a button in the form. Looks like an IE9 specific bug. 33577 var handleFormSubmission = function(event) { 33578 scope.$apply(function() { 33579 controller.$commitViewValue(); 33580 controller.$setSubmitted(); 33581 }); 33582 33583 event.preventDefault(); 33584 }; 33585 33586 formElement[0].addEventListener('submit', handleFormSubmission); 33587 33588 // unregister the preventDefault listener so that we don't not leak memory but in a 33589 // way that will achieve the prevention of the default action. 33590 formElement.on('$destroy', function() { 33591 $timeout(function() { 33592 formElement[0].removeEventListener('submit', handleFormSubmission); 33593 }, 0, false); 33594 }); 33595 } 33596 33597 var parentFormCtrl = ctrls[1] || controller.$$parentForm; 33598 parentFormCtrl.$addControl(controller); 33599 33600 var setter = nameAttr ? getSetter(controller.$name) : noop; 33601 33602 if (nameAttr) { 33603 setter(scope, controller); 33604 attr.$observe(nameAttr, function(newValue) { 33605 if (controller.$name === newValue) return; 33606 setter(scope, undefined); 33607 controller.$$parentForm.$$renameControl(controller, newValue); 33608 setter = getSetter(controller.$name); 33609 setter(scope, controller); 33610 }); 33611 } 33612 formElement.on('$destroy', function() { 33613 controller.$$parentForm.$removeControl(controller); 33614 setter(scope, undefined); 33615 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards 33616 }); 33617 } 33618 }; 33619 } 33620 }; 33621 33622 return formDirective; 33623 33624 function getSetter(expression) { 33625 if (expression === '') { 33626 //create an assignable expression, so forms with an empty name can be renamed later 33627 return $parse('this[""]').assign; 33628 } 33629 return $parse(expression).assign || noop; 33630 } 33631 }]; 33632}; 33633 33634var formDirective = formDirectiveFactory(); 33635var ngFormDirective = formDirectiveFactory(true); 33636 33637 33638 33639// helper methods 33640function setupValidity(instance) { 33641 instance.$$classCache = {}; 33642 instance.$$classCache[INVALID_CLASS] = !(instance.$$classCache[VALID_CLASS] = instance.$$element.hasClass(VALID_CLASS)); 33643} 33644function addSetValidityMethod(context) { 33645 var clazz = context.clazz, 33646 set = context.set, 33647 unset = context.unset; 33648 33649 clazz.prototype.$setValidity = function(validationErrorKey, state, controller) { 33650 if (isUndefined(state)) { 33651 createAndSet(this, '$pending', validationErrorKey, controller); 33652 } else { 33653 unsetAndCleanup(this, '$pending', validationErrorKey, controller); 33654 } 33655 if (!isBoolean(state)) { 33656 unset(this.$error, validationErrorKey, controller); 33657 unset(this.$$success, validationErrorKey, controller); 33658 } else { 33659 if (state) { 33660 unset(this.$error, validationErrorKey, controller); 33661 set(this.$$success, validationErrorKey, controller); 33662 } else { 33663 set(this.$error, validationErrorKey, controller); 33664 unset(this.$$success, validationErrorKey, controller); 33665 } 33666 } 33667 if (this.$pending) { 33668 cachedToggleClass(this, PENDING_CLASS, true); 33669 this.$valid = this.$invalid = undefined; 33670 toggleValidationCss(this, '', null); 33671 } else { 33672 cachedToggleClass(this, PENDING_CLASS, false); 33673 this.$valid = isObjectEmpty(this.$error); 33674 this.$invalid = !this.$valid; 33675 toggleValidationCss(this, '', this.$valid); 33676 } 33677 33678 // re-read the state as the set/unset methods could have 33679 // combined state in this.$error[validationError] (used for forms), 33680 // where setting/unsetting only increments/decrements the value, 33681 // and does not replace it. 33682 var combinedState; 33683 if (this.$pending && this.$pending[validationErrorKey]) { 33684 combinedState = undefined; 33685 } else if (this.$error[validationErrorKey]) { 33686 combinedState = false; 33687 } else if (this.$$success[validationErrorKey]) { 33688 combinedState = true; 33689 } else { 33690 combinedState = null; 33691 } 33692 33693 toggleValidationCss(this, validationErrorKey, combinedState); 33694 this.$$parentForm.$setValidity(validationErrorKey, combinedState, this); 33695 }; 33696 33697 function createAndSet(ctrl, name, value, controller) { 33698 if (!ctrl[name]) { 33699 ctrl[name] = {}; 33700 } 33701 set(ctrl[name], value, controller); 33702 } 33703 33704 function unsetAndCleanup(ctrl, name, value, controller) { 33705 if (ctrl[name]) { 33706 unset(ctrl[name], value, controller); 33707 } 33708 if (isObjectEmpty(ctrl[name])) { 33709 ctrl[name] = undefined; 33710 } 33711 } 33712 33713 function cachedToggleClass(ctrl, className, switchValue) { 33714 if (switchValue && !ctrl.$$classCache[className]) { 33715 ctrl.$$animate.addClass(ctrl.$$element, className); 33716 ctrl.$$classCache[className] = true; 33717 } else if (!switchValue && ctrl.$$classCache[className]) { 33718 ctrl.$$animate.removeClass(ctrl.$$element, className); 33719 ctrl.$$classCache[className] = false; 33720 } 33721 } 33722 33723 function toggleValidationCss(ctrl, validationErrorKey, isValid) { 33724 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; 33725 33726 cachedToggleClass(ctrl, VALID_CLASS + validationErrorKey, isValid === true); 33727 cachedToggleClass(ctrl, INVALID_CLASS + validationErrorKey, isValid === false); 33728 } 33729} 33730 33731function isObjectEmpty(obj) { 33732 if (obj) { 33733 for (var prop in obj) { 33734 if (obj.hasOwnProperty(prop)) { 33735 return false; 33736 } 33737 } 33738 } 33739 return true; 33740} 33741 33742/* global 33743 VALID_CLASS: false, 33744 INVALID_CLASS: false, 33745 PRISTINE_CLASS: false, 33746 DIRTY_CLASS: false, 33747 ngModelMinErr: false 33748*/ 33749 33750// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 33751var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/; 33752// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) 33753// Note: We are being more lenient, because browsers are too. 33754// 1. Scheme 33755// 2. Slashes 33756// 3. Username 33757// 4. Password 33758// 5. Hostname 33759// 6. Port 33760// 7. Path 33761// 8. Query 33762// 9. Fragment 33763// 1111111111111111 222 333333 44444 55555555555555555555555 666 77777777 8888888 999 33764var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i; 33765// eslint-disable-next-line max-len 33766var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/; 33767var NUMBER_REGEXP = /^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/; 33768var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/; 33769var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; 33770var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/; 33771var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/; 33772var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; 33773 33774var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown'; 33775var PARTIAL_VALIDATION_TYPES = createMap(); 33776forEach('date,datetime-local,month,time,week'.split(','), function(type) { 33777 PARTIAL_VALIDATION_TYPES[type] = true; 33778}); 33779 33780var inputType = { 33781 33782 /** 33783 * @ngdoc input 33784 * @name input[text] 33785 * 33786 * @description 33787 * Standard HTML text input with
33787angular data binding, inherited by most of the `input` elements. 33788 * 33789 * 33790 * @param {string} ngModel Assignable angular expression to data-bind to. 33791 * @param {string=} name Property name of the form under which the control is published. 33792 * @param {string=} required Adds `required` validation error key if the value is not entered. 33793 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 33794 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 33795 * `required` when you want to data-bind to the `required` attribute. 33796 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 33797 * minlength. 33798 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 33799 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of 33800 * any length. 33801 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string 33802 * that contains the regular expression body that will be converted to a regular expression 33803 * as in the ngPattern directive. 33804 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} 33805 * does not match a RegExp found by evaluating the Angular expression given in the attribute value. 33806 * If the expression evaluates to a RegExp object, then this is used directly. 33807 * If the expression evaluates to a string, then it will be converted to a RegExp 33808 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to 33809 * `new RegExp('^abc$')`.<br /> 33810 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to 33811 * start at the index of the last search's match, thus not taking the whole input value into 33812 * account. 33813 * @param {string=} ngChange Angular expression to be executed when input changes due to user 33814 * interaction with the input element. 33815 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. 33816 * This parameter is ignored for input[type=password] controls, which will never trim the 33817 * input. 33818 * 33819 * @example 33820 <example name="text-input-directive" module="textInputExample"> 33821 <file name="index.html"> 33822 <script> 33823 angular.module('textInputExample', []) 33824 .controller('ExampleController', ['$scope', function($scope) { 33825 $scope.example = { 33826 text: 'guest', 33827 word: /^\s*\w*\s*$/ 33828 }; 33829 }]); 33830 </script>
33831 <form name="myForm" ng-controller="ExampleController"> 33832 <label>Single word: 33833 <input type="text" name="input" ng-model="example.text" 33834 ng-pattern="example.word" required ng-trim="false"> 33835 </label> 33836 <div role="alert"> 33837 <span class="error" ng-show="myForm.input.$error.required"> 33838 Required!</span> 33839 <span class="error" ng-show="myForm.input.$error.pattern"> 33840 Single word only!</span> 33841 </div> 33842 <code>text = {{example.text}}</code><br/> 33843 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br/> 33844 <code>myForm.input.$error = {{myForm.input.$error}}</code><br/> 33845 <code>myForm.$valid = {{myForm.$valid}}</code><br/> 33846 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br/> 33847 </form> 33848 </file> 33849 <file name="protractor.js" type="protractor"> 33850 var text = element(by.binding('example.text')); 33851 var valid = element(by.binding('myForm.input.$valid')); 33852 var input = element(by.model('example.text')); 33853 33854 it('should initialize to model', function() { 33855 expect(text.getText()).toContain('guest'); 33856 expect(valid.getText()).toContain('true'); 33857 }); 33858 33859 it('should be invalid if empty', function() { 33860 input.clear(); 33861 input.sendKeys(''); 33862 33863 expect(text.getText()).toEqual('text ='); 33864 expect(valid.getText()).toContain('false'); 33865 }); 33866 33867 it('should be invalid if multi word', function() { 33868 input.clear(); 33869 input.sendKeys('hello world'); 33870 33871 expect(valid.getText()).toContain('false'); 33872 }); 33873 </file> 33874 </example> 33875 */ 33876 'text': textInputType, 33877 33878 /** 33879 * @ngdoc input 33880 * @name input[date] 33881 * 33882 * @description 33883 * Input with date validation and transformation. In browsers that do not yet support 33884 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 33885 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many 33886 * modern browsers do not yet support this input type, it is important to provide cues to users on the 33887 * expected input format via a placeholder or label. 33888 * 33889 * The model must always be a Date object, otherwise Angular will throw an error. 33890 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. 33891 * 33892 * The timezone to be used to read/write the `Date` instance in the model can be defined using 33893 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. 33894 * 33895 * @param {string} ngModel Assignable angular expression to data-bind to. 33896 * @param {string=} name Property name of the form under which the control is published. 33897 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a 33898 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute 33899 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5 33900 * constraint validation. 33901 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be 33902 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute 33903 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5 33904 * constraint validation. 33905 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string 33906 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. 33907 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string 33908 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. 33909 * @param {string=} required Sets `required` validation error key if the value is not entered. 33910 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 33911 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 33912 * `required` when you want to data-bind to the `required` attribute. 33913 * @param {string=} ngChange Angular expression to be executed when input changes due to user 33914 * interaction with the input element. 33915 * 33916 * @example 33917 <example name="date-input-directive" module="dateInputExample"> 33918 <file name="index.html"> 33919 <script> 33920 angular.module('dateInputExample', []) 33921 .controller('DateController', ['$scope', function($scope) { 33922 $scope.example = { 33923 value: new Date(2013, 9, 22) 33924 }; 33925 }]); 33926 </script> 33927 <form name="myForm" ng-controller="DateController as dateCtrl"> 33928 <label for="exampleInput">Pick a date in 2013:</label> 33929 <input type="date" id="exampleInput" name="input" ng-model="example.value" 33930 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required /> 33931 <div role="alert">
33932 <span class="error" ng-show="myForm.input.$error.required"> 33933 Required!</span> 33934 <span class="error" ng-show="myForm.input.$error.date"> 33935 Not a valid date!</span> 33936 </div> 33937 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/> 33938 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 33939 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 33940 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 33941 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 33942 </form> 33943 </file> 33944 <file name="protractor.js" type="protractor"> 33945 var value = element(by.binding('example.value | date: "yyyy-MM-dd"')); 33946 var valid = element(by.binding('myForm.input.$valid')); 33947 33948 // currently protractor/webdriver does not support 33949 // sending keys to all known HTML5 input controls 33950 // for various browsers (see https://github.com/angular/protractor/issues/562). 33951 function setInput(val) { 33952 // set the value of the element and force validation. 33953 var scr = "var ipt = document.getElementById('exampleInput'); " + 33954 "ipt.value = '" + val + "';" + 33955 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; 33956 browser.executeScript(scr); 33957 } 33958 33959 it('should initialize to model', function() { 33960 expect(value.getText()).toContain('2013-10-22'); 33961 expect(valid.getText()).toContain('myForm.input.$valid = true'); 33962 }); 33963 33964 it('should be invalid if empty', function() { 33965 setInput(''); 33966 expect(value.getText()).toEqual('value ='); 33967 expect(valid.getText()).toContain('myForm.input.$valid = false'); 33968 }); 33969 33970 it('should be invalid if over max', function() { 33971 setInput('2015-01-01'); 33972 expect(value.getText()).toContain(''); 33973 expect(valid.getText()).toContain('myForm.input.$valid = false'); 33974 }); 33975 </file> 33976 </example> 33977 */ 33978 'date': createDateInputType('date', DATE_REGEXP, 33979 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), 33980 'yyyy-MM-dd'), 33981 33982 /** 33983 * @ngdoc input 33984 * @name input[datetime-local] 33985 * 33986 * @description 33987 * Input with datetime validation and transformation. In browsers that do not yet support 33988 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 33989 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. 33990 * 33991 * The model must always be a Date object, otherwise Angular will throw an error. 33992 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. 33993 * 33994 * The timezone to be used to read/write the `Date` instance in the model can be defined using 33995 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. 33996 * 33997 * @param {string} ngModel Assignable angular expression to data-bind to. 33998 * @param {string=} name Property name of the form under which the control is published. 33999 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. 34000 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation 34001 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). 34002 * Note that `min` will also add native HTML5 constraint validation. 34003 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. 34004 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation 34005 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). 34006 * Note that `max` will also add native HTML5 constraint validation. 34007 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string 34008 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. 34009 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string 34010 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. 34011 * @param {string=} required Sets `required` validation error key if the value is not entered. 34012 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 34013 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 34014 * `required` when you want to data-bind to the `required` attribute. 34015 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34016 * interaction with the input element. 34017 * 34018 * @example 34019 <example name="datetimelocal-input-directive" module="dateExample"> 34020 <file name="index.html"> 34021 <script> 34022 angular.module('dateExample', []) 34023 .controller('DateController', ['$scope', function($scope) { 34024 $scope.example = { 34025 value: new Date(2010, 11, 28, 14, 57) 34026 }; 34027 }]); 34028 </script> 34029 <form name="myForm" ng-controller="DateController as dateCtrl"> 34030 <label for="exampleInput">Pick a date between in 2013:</label> 34031 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value" 34032 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required /> 34033 <div role="alert">
34034 <span class="error" ng-show="myForm.input.$error.required"> 34035 Required!</span> 34036 <span class="error" ng-show="myForm.input.$error.datetimelocal"> 34037 Not a valid date!</span> 34038 </div> 34039 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/> 34040 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 34041 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 34042 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 34043 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 34044 </form> 34045 </file> 34046 <file name="protractor.js" type="protractor"> 34047 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"')); 34048 var valid = element(by.binding('myForm.input.$valid')); 34049 34050 // currently protractor/webdriver does not support 34051 // sending keys to all known HTML5 input controls 34052 // for various browsers (https://github.com/angular/protractor/issues/562). 34053 function setInput(val) { 34054 // set the value of the element and force validation. 34055 var scr = "var ipt = document.getElementById('exampleInput'); " + 34056 "ipt.value = '" + val + "';" + 34057 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; 34058 browser.executeScript(scr); 34059 } 34060 34061 it('should initialize to model', function() { 34062 expect(value.getText()).toContain('2010-12-28T14:57:00'); 34063 expect(valid.getText()).toContain('myForm.input.$valid = true'); 34064 }); 34065 34066 it('should be invalid if empty', function() { 34067 setInput(''); 34068 expect(value.getText()).toEqual('value ='); 34069 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34070 }); 34071 34072 it('should be invalid if over max', function() { 34073 setInput('2015-01-01T23:59:00'); 34074 expect(value.getText()).toContain(''); 34075 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34076 }); 34077 </file> 34078 </example> 34079 */ 34080 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP, 34081 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']), 34082 'yyyy-MM-ddTHH:mm:ss.sss'), 34083 34084 /** 34085 * @ngdoc input 34086 * @name input[time] 34087 * 34088 * @description 34089 * Input with time validation and transformation. In browsers that do not yet support 34090 * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 34091 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a 34092 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. 34093 * 34094 * The model must always be a Date object, otherwise Angular will throw an error. 34095 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. 34096 * 34097 * The timezone to be used to read/write the `Date` instance in the model can be defined using 34098 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. 34099 * 34100 * @param {string} ngModel Assignable angular expression to data-bind to. 34101 * @param {string=} name Property name of the form under which the control is published. 34102 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. 34103 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this 34104 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add 34105 * native HTML5 constraint validation. 34106 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. 34107 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this 34108 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add 34109 * native HTML5 constraint validation. 34110 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the 34111 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute. 34112 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the 34113 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute. 34114 * @param {string=} required Sets `required` validation error key if the value is not entered. 34115 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 34116 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 34117 * `required` when you want to data-bind to the `required` attribute. 34118 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34119 * interaction with the input element. 34120 * 34121 * @example 34122 <example name="time-input-directive" module="timeExample"> 34123 <file name="index.html"> 34124 <script> 34125 angular.module('timeExample', []) 34126 .controller('DateController', ['$scope', function($scope) { 34127 $scope.example = { 34128 value: new Date(1970, 0, 1, 14, 57, 0) 34129 }; 34130 }]); 34131 </script> 34132 <form name="myForm" ng-controller="DateController as dateCtrl"> 34133 <label for="exampleInput">Pick a time between 8am and 5pm:</label> 34134 <input type="time" id="exampleInput" name="input" ng-model="example.value" 34135 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required /> 34136 <div role="alert">
34137 <span class="error" ng-show="myForm.input.$error.required"> 34138 Required!</span> 34139 <span class="error" ng-show="myForm.input.$error.time"> 34140 Not a valid date!</span> 34141 </div> 34142 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/> 34143 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 34144 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 34145 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 34146 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 34147 </form> 34148 </file> 34149 <file name="protractor.js" type="protractor"> 34150 var value = element(by.binding('example.value | date: "HH:mm:ss"')); 34151 var valid = element(by.binding('myForm.input.$valid')); 34152 34153 // currently protractor/webdriver does not support 34154 // sending keys to all known HTML5 input controls 34155 // for various browsers (https://github.com/angular/protractor/issues/562). 34156 function setInput(val) { 34157 // set the value of the element and force validation. 34158 var scr = "var ipt = document.getElementById('exampleInput'); " + 34159 "ipt.value = '" + val + "';" + 34160 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; 34161 browser.executeScript(scr); 34162 } 34163 34164 it('should initialize to model', function() { 34165 expect(value.getText()).toContain('14:57:00'); 34166 expect(valid.getText()).toContain('myForm.input.$valid = true'); 34167 }); 34168 34169 it('should be invalid if empty', function() { 34170 setInput(''); 34171 expect(value.getText()).toEqual('value ='); 34172 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34173 }); 34174 34175 it('should be invalid if over max', function() { 34176 setInput('23:59:00'); 34177 expect(value.getText()).toContain(''); 34178 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34179 }); 34180 </file> 34181 </example> 34182 */ 34183 'time': createDateInputType('time', TIME_REGEXP, 34184 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']), 34185 'HH:mm:ss.sss'), 34186 34187 /** 34188 * @ngdoc input 34189 * @name input[week] 34190 * 34191 * @description 34192 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support 34193 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 34194 * week format (yyyy-W##), for example: `2013-W02`. 34195 * 34196 * The model must always be a Date object, otherwise Angular will throw an error. 34197 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. 34198 * 34199 * The timezone to be used to read/write the `Date` instance in the model can be defined using 34200 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. 34201 * 34202 * @param {string} ngModel Assignable angular expression to data-bind to. 34203 * @param {string=} name Property name of the form under which the control is published. 34204 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. 34205 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this 34206 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add 34207 * native HTML5 constraint validation. 34208 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. 34209 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this 34210 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add 34211 * native HTML5 constraint validation. 34212 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string 34213 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. 34214 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string 34215 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. 34216 * @param {string=} required Sets `required` validation error key if the value is not entered. 34217 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 34218 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 34219 * `required` when you want to data-bind to the `required` attribute. 34220 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34221 * interaction with the input element. 34222 * 34223 * @example 34224 <example name="week-input-directive" module="weekExample"> 34225 <file name="index.html"> 34226 <script> 34227 angular.module('weekExample', []) 34228 .controller('DateController', ['$scope', function($scope) { 34229 $scope.example = { 34230 value: new Date(2013, 0, 3) 34231 }; 34232 }]); 34233 </script> 34234 <form name="myForm" ng-controller="DateController as dateCtrl"> 34235 <label>Pick a date between in 2013: 34236 <input id="exampleInput" type="week" name="input" ng-model="example.value" 34237 placeholder="YYYY-W##" min="2012-W32" 34238 max="2013-W52" required /> 34239 </label> 34240 <div role="alert">
34241 <span class="error" ng-show="myForm.input.$error.required"> 34242 Required!</span> 34243 <span class="error" ng-show="myForm.input.$error.week"> 34244 Not a valid date!</span> 34245 </div> 34246 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/> 34247 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 34248 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 34249 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 34250 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 34251 </form> 34252 </file> 34253 <file name="protractor.js" type="protractor"> 34254 var value = element(by.binding('example.value | date: "yyyy-Www"')); 34255 var valid = element(by.binding('myForm.input.$valid')); 34256 34257 // currently protractor/webdriver does not support 34258 // sending keys to all known HTML5 input controls 34259 // for various browsers (https://github.com/angular/protractor/issues/562). 34260 function setInput(val) { 34261 // set the value of the element and force validation. 34262 var scr = "var ipt = document.getElementById('exampleInput'); " + 34263 "ipt.value = '" + val + "';" + 34264 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; 34265 browser.executeScript(scr); 34266 } 34267 34268 it('should initialize to model', function() { 34269 expect(value.getText()).toContain('2013-W01'); 34270 expect(valid.getText()).toContain('myForm.input.$valid = true'); 34271 }); 34272 34273 it('should be invalid if empty', function() { 34274 setInput(''); 34275 expect(value.getText()).toEqual('value ='); 34276 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34277 }); 34278 34279 it('should be invalid if over max', function() { 34280 setInput('2015-W01'); 34281 expect(value.getText()).toContain(''); 34282 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34283 }); 34284 </file> 34285 </example> 34286 */ 34287 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'), 34288 34289 /** 34290 * @ngdoc input 34291 * @name input[month] 34292 * 34293 * @description 34294 * Input with month validation and transformation. In browsers that do not yet support 34295 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 34296 * month format (yyyy-MM), for example: `2009-01`. 34297 * 34298 * The model must always be a Date object, otherwise Angular will throw an error. 34299 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. 34300 * If the model is not set to the first of the month, the next view to model update will set it 34301 * to the first of the month. 34302 * 34303 * The timezone to be used to read/write the `Date` instance in the model can be defined using 34304 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. 34305 * 34306 * @param {string} ngModel Assignable angular expression to data-bind to. 34307 * @param {string=} name Property name of the form under which the control is published. 34308 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. 34309 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this 34310 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add 34311 * native HTML5 constraint validation. 34312 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. 34313 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this 34314 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add 34315 * native HTML5 constraint validation. 34316 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string 34317 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. 34318 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string 34319 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. 34320 34321 * @param {string=} required Sets `required` validation error key if the value is not entered. 34322 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 34323 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 34324 * `required` when you want to data-bind to the `required` attribute. 34325 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34326 * interaction with the input element. 34327 * 34328 * @example 34329 <example name="month-input-directive" module="monthExample"> 34330 <file name="index.html"> 34331 <script> 34332 angular.module('monthExample', []) 34333 .controller('DateController', ['$scope', function($scope) { 34334 $scope.example = { 34335 value: new Date(2013, 9, 1) 34336 }; 34337 }]); 34338 </script> 34339 <form name="myForm" ng-controller="DateController as dateCtrl"> 34340 <label for="exampleInput">Pick a month in 2013:</label> 34341 <input id="exampleInput" type="month" name="input" ng-model="example.value" 34342 placeholder="yyyy-MM" min="2013-01" max="2013-12" required /> 34343 <div role="alert">
34344 <span class="error" ng-show="myForm.input.$error.required"> 34345 Required!</span> 34346 <span class="error" ng-show="myForm.input.$error.month"> 34347 Not a valid month!</span> 34348 </div> 34349 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/> 34350 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 34351 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 34352 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 34353 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 34354 </form> 34355 </file> 34356 <file name="protractor.js" type="protractor"> 34357 var value = element(by.binding('example.value | date: "yyyy-MM"')); 34358 var valid = element(by.binding('myForm.input.$valid')); 34359 34360 // currently protractor/webdriver does not support 34361 // sending keys to all known HTML5 input controls 34362 // for various browsers (https://github.com/angular/protractor/issues/562). 34363 function setInput(val) { 34364 // set the value of the element and force validation. 34365 var scr = "var ipt = document.getElementById('exampleInput'); " + 34366 "ipt.value = '" + val + "';" + 34367 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; 34368 browser.executeScript(scr); 34369 } 34370 34371 it('should initialize to model', function() { 34372 expect(value.getText()).toContain('2013-10'); 34373 expect(valid.getText()).toContain('myForm.input.$valid = true'); 34374 }); 34375 34376 it('should be invalid if empty', function() { 34377 setInput(''); 34378 expect(value.getText()).toEqual('value ='); 34379 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34380 }); 34381 34382 it('should be invalid if over max', function() { 34383 setInput('2015-01'); 34384 expect(value.getText()).toContain(''); 34385 expect(valid.getText()).toContain('myForm.input.$valid = false'); 34386 }); 34387 </file> 34388 </example> 34389 */ 34390 'month': createDateInputType('month', MONTH_REGEXP, 34391 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']), 34392 'yyyy-MM'), 34393 34394 /** 34395 * @ngdoc input 34396 * @name input[number] 34397 * 34398 * @description 34399 * Text input with number validation and transformation. Sets the `number` validation 34400 * error if not a valid number. 34401 * 34402 * <div class="alert alert-warning"> 34403 * The model must always be of type `number` otherwise Angular will throw an error. 34404 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt} 34405 * error docs for more information and an example of how to convert your model if necessary. 34406 * </div> 34407 * 34408 * ## Issues with HTML5 constraint validation 34409 * 34410 * In browsers that follow the 34411 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29), 34412 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}. 34413 * If a non-number is entered in the input, the browser will report the value as an empty string, 34414 * which means the view / model values in `ngModel` and subsequently the scope value 34415 * will also be an empty string. 34416 * 34417 * 34418 * @param {string} ngModel Assignable angular expression to data-bind to. 34419 * @param {string=} name Property name of the form under which the control is published. 34420 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. 34421 * Can be interpolated. 34422 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. 34423 * Can be interpolated. 34424 * @param {string=} ngMin Like `min`, sets the `min` validation error key if the value entered is less than `ngMin`, 34425 * but does not trigger HTML5 native validation. Takes an expression. 34426 * @param {string=} ngMax Like `max`, sets the `max` validation error key if the value entered is greater than `ngMax`, 34427 * but does not trigger HTML5 native validation. Takes an expression. 34428 * @param {string=} step Sets the `step` validation error key if the value entered does not fit the `step` constraint. 34429 * Can be interpolated. 34430 * @param {string=} ngStep Like `step`, sets the `step` validation error key if the value entered does not fit the `ngStep` constraint, 34431 * but does not trigger HTML5 native validation. Takes an expression. 34432 * @param {string=} required Sets `required` validation error key if the value is not entered. 34433 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 34434 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 34435 * `required` when you want to data-bind to the `required` attribute. 34436 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 34437 * minlength. 34438 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 34439 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of 34440 * any length. 34441 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string 34442 * that contains the regular expression body that will be converted to a regular expression 34443 * as in the ngPattern directive. 34444 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} 34445 * does not match a RegExp found by evaluating the Angular expression given in the attribute value. 34446 * If the expression evaluates to a RegExp object, then this is used directly. 34447 * If the expression evaluates to a string, then it will be converted to a RegExp 34448 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to 34449 * `new RegExp('^abc$')`.<br /> 34450 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to 34451 * start at the index of the last search's match, thus not taking the whole input value into 34452 * account. 34453 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34454 * interaction with the input element. 34455 * 34456 * @example 34457 <example name="number-input-directive" module="numberExample"> 34458 <file name="index.html"> 34459 <script> 34460 angular.module('numberExample', [])
34461 .controller('ExampleController', ['$scope', function($scope) { 34462 $scope.example = { 34463 value: 12 34464 }; 34465 }]); 34466 </script> 34467 <form name="myForm" ng-controller="ExampleController"> 34468 <label>Number: 34469 <input type="number" name="input" ng-model="example.value" 34470 min="0" max="99" required> 34471 </label> 34472 <div role="alert"> 34473 <span class="error" ng-show="myForm.input.$error.required"> 34474 Required!</span> 34475 <span class="error" ng-show="myForm.input.$error.number"> 34476 Not valid number!</span> 34477 </div> 34478 <tt>value = {{example.value}}</tt><br/> 34479 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 34480 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 34481 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 34482 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 34483 </form> 34484 </file> 34485 <file name="protractor.js" type="protractor"> 34486 var value = element(by.binding('example.value')); 34487 var valid = element(by.binding('myForm.input.$valid')); 34488 var input = element(by.model('example.value')); 34489 34490 it('should initialize to model', function() { 34491 expect(value.getText()).toContain('12'); 34492 expect(valid.getText()).toContain('true'); 34493 }); 34494 34495 it('should be invalid if empty', function() { 34496 input.clear(); 34497 input.sendKeys(''); 34498 expect(value.getText()).toEqual('value ='); 34499 expect(valid.getText()).toContain('false'); 34500 }); 34501 34502 it('should be invalid if over max', function() { 34503 input.clear(); 34504 input.sendKeys('123'); 34505 expect(value.getText()).toEqual('value ='); 34506 expect(valid.getText()).toContain('false'); 34507 }); 34508 </file> 34509 </example> 34510 */ 34511 'number': numberInputType, 34512 34513 34514 /** 34515 * @ngdoc input 34516 * @name input[url] 34517 * 34518 * @description 34519 * Text input with URL validation. Sets the `url` validation error key if the content is not a 34520 * valid URL. 34521 * 34522 * <div class="alert alert-warning"> 34523 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex 34524 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify 34525 * the built-in validators (see the {@link guide/forms Forms guide}) 34526 * </div> 34527 * 34528 * @param {string} ngModel Assignable angular expression to data-bind to. 34529 * @param {string=} name Property name of the form under which the control is published. 34530 * @param {string=} required Sets `required` validation error key if the value is not entered. 34531 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 34532 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 34533 * `required` when you want to data-bind to the `required` attribute. 34534 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 34535 * minlength. 34536 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 34537 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of 34538 * any length. 34539 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string 34540 * that contains the regular expression body that will be converted to a regular expression 34541 * as in the ngPattern directive. 34542 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} 34543 * does not match a RegExp found by evaluating the Angular expression given in the attribute value. 34544 * If the expression evaluates to a RegExp object, then this is used directly. 34545 * If the expression evaluates to a string, then it will be converted to a RegExp 34546 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to 34547 * `new RegExp('^abc$')`.<br /> 34548 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to 34549 * start at the index of the last search's match, thus not taking the whole input value into 34550 * account. 34551 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34552 * interaction with the input element. 34553 * 34554 * @example 34555 <example name="url-input-directive" module="urlExample"> 34556 <file name="index.html"> 34557 <script> 34558 angular.module('urlExample', [])
34559 .controller('ExampleController', ['$scope', function($scope) { 34560 $scope.url = { 34561 text: 'http://google.com' 34562 }; 34563 }]); 34564 </script> 34565 <form name="myForm" ng-controller="ExampleController"> 34566 <label>URL: 34567 <input type="url" name="input" ng-model="url.text" required> 34568 <label> 34569 <div role="alert"> 34570 <span class="error" ng-show="myForm.input.$error.required"> 34571 Required!</span> 34572 <span class="error" ng-show="myForm.input.$error.url"> 34573 Not valid url!</span> 34574 </div> 34575 <tt>text = {{url.text}}</tt><br/> 34576 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 34577 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 34578 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 34579 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 34580 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/> 34581 </form> 34582 </file> 34583 <file name="protractor.js" type="protractor"> 34584 var text = element(by.binding('url.text')); 34585 var valid = element(by.binding('myForm.input.$valid')); 34586 var input = element(by.model('url.text')); 34587 34588 it('should initialize to model', function() { 34589 expect(text.getText()).toContain('http://google.com'); 34590 expect(valid.getText()).toContain('true'); 34591 }); 34592 34593 it('should be invalid if empty', function() { 34594 input.clear(); 34595 input.sendKeys(''); 34596 34597 expect(text.getText()).toEqual('text ='); 34598 expect(valid.getText()).toContain('false'); 34599 }); 34600 34601 it('should be invalid if not url', function() { 34602 input.clear(); 34603 input.sendKeys('box'); 34604 34605 expect(valid.getText()).toContain('false'); 34606 }); 34607 </file> 34608 </example> 34609 */ 34610 'url': urlInputType, 34611 34612 34613 /** 34614 * @ngdoc input 34615 * @name input[email] 34616 * 34617 * @description 34618 * Text input with email validation. Sets the `email` validation error key if not a valid email 34619 * address. 34620 * 34621 * <div class="alert alert-warning"> 34622 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex 34623 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can 34624 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide}) 34625 * </div> 34626 * 34627 * @param {string} ngModel Assignable angular expression to data-bind to. 34628 * @param {string=} name Property name of the form under which the control is published. 34629 * @param {string=} required Sets `required` validation error key if the value is not entered. 34630 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 34631 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 34632 * `required` when you want to data-bind to the `required` attribute. 34633 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 34634 * minlength. 34635 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 34636 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of 34637 * any length. 34638 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string 34639 * that contains the regular expression body that will be converted to a regular expression 34640 * as in the ngPattern directive. 34641 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} 34642 * does not match a RegExp found by evaluating the Angular expression given in the attribute value. 34643 * If the expression evaluates to a RegExp object, then this is used directly. 34644 * If the expression evaluates to a string, then it will be converted to a RegExp 34645 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to 34646 * `new RegExp('^abc$')`.<br /> 34647 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to 34648 * start at the index of the last search's match, thus not taking the whole input value into 34649 * account. 34650 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34651 * interaction with the input element. 34652 * 34653 * @example 34654 <example name="email-input-directive" module="emailExample"> 34655 <file name="index.html"> 34656 <script> 34657 angular.module('emailExample', [])
34658 .controller('ExampleController', ['$scope', function($scope) { 34659 $scope.email = { 34660 text: '[email protected]' 34661 }; 34662 }]); 34663 </script> 34664 <form name="myForm" ng-controller="ExampleController"> 34665 <label>Email: 34666 <input type="email" name="input" ng-model="email.text" required> 34667 </label> 34668 <div role="alert"> 34669 <span class="error" ng-show="myForm.input.$error.required"> 34670 Required!</span> 34671 <span class="error" ng-show="myForm.input.$error.email"> 34672 Not valid email!</span> 34673 </div> 34674 <tt>text = {{email.text}}</tt><br/> 34675 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 34676 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 34677 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 34678 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 34679 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/> 34680 </form> 34681 </file> 34682 <file name="protractor.js" type="protractor"> 34683 var text = element(by.binding('email.text')); 34684 var valid = element(by.binding('myForm.input.$valid')); 34685 var input = element(by.model('email.text')); 34686 34687 it('should initialize to model', function() { 34688 expect(text.getText()).toContain('[email protected]'); 34689 expect(valid.getText()).toContain('true'); 34690 }); 34691 34692 it('should be invalid if empty', function() { 34693 input.clear(); 34694 input.sendKeys(''); 34695 expect(text.getText()).toEqual('text ='); 34696 expect(valid.getText()).toContain('false'); 34697 }); 34698 34699 it('should be invalid if not email', function() { 34700 input.clear(); 34701 input.sendKeys('xxx'); 34702 34703 expect(valid.getText()).toContain('false'); 34704 }); 34705 </file> 34706 </example> 34707 */ 34708 'email': emailInputType, 34709 34710 34711 /** 34712 * @ngdoc input 34713 * @name input[radio] 34714 * 34715 * @description 34716 * HTML radio button. 34717 * 34718 * @param {string} ngModel Assignable angular expression to data-bind to. 34719 * @param {string} value The value to which the `ngModel` expression should be set when selected. 34720 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string, 34721 * too. Use `ngValue` if you need complex models (`number`, `object`, ...). 34722 * @param {string=} name Property name of the form under which the control is published. 34723 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34724 * interaction with the input element. 34725 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio 34726 * is selected. Should be used instead of the `value` attribute if you need 34727 * a non-string `ngModel` (`boolean`, `array`, ...). 34728 * 34729 * @example 34730 <example name="radio-input-directive" module="radioExample"> 34731 <file name="index.html"> 34732 <script> 34733 angular.module('radioExample', []) 34734 .controller('ExampleController', ['$scope', function($scope) { 34735 $scope.color = { 34736 name: 'blue' 34737 }; 34738 $scope.specialValue = { 34739 "id": "12345", 34740 "value": "green" 34741 }; 34742 }]); 34743 </script>
34744 <form name="myForm" ng-controller="ExampleController"> 34745 <label> 34746 <input type="radio" ng-model="color.name" value="red"> 34747 Red 34748 </label><br/> 34749 <label> 34750 <input type="radio" ng-model="color.name" ng-value="specialValue"> 34751 Green 34752 </label><br/> 34753 <label> 34754 <input type="radio" ng-model="color.name" value="blue"> 34755 Blue 34756 </label><br/> 34757 <tt>color = {{color.name | json}}</tt><br/> 34758 </form> 34759 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. 34760 </file> 34761 <file name="protractor.js" type="protractor"> 34762 it('should change state', function() { 34763 var inputs = element.all(by.model('color.name')); 34764 var color = element(by.binding('color.name')); 34765 34766 expect(color.getText()).toContain('blue'); 34767 34768 inputs.get(0).click(); 34769 expect(color.getText()).toContain('red'); 34770 34771 inputs.get(1).click(); 34772 expect(color.getText()).toContain('green'); 34773 }); 34774 </file> 34775 </example> 34776 */ 34777 'radio': radioInputType, 34778 34779 /** 34780 * @ngdoc input 34781 * @name input[range] 34782 * 34783 * @description 34784 * Native range input with validation and transformation. 34785 * 34786 * The model for the range input must always be a `Number`. 34787 * 34788 * IE9 and other browsers that do not support the `range` type fall back 34789 * to a text input without any default values for `min`, `max` and `step`. Model binding, 34790 * validation and number parsing are nevertheless supported. 34791 * 34792 * Browsers that support range (latest Chrome, Safari, Firefox, Edge) treat `input[range]` 34793 * in a way that never allows the input to hold an invalid value. That means: 34794 * - any non-numerical value is set to `(max + min) / 2`. 34795 * - any numerical value that is less than the current min val, or greater than the current max val 34796 * is set to the min / max val respectively. 34797 * - additionally, the current `step` is respected, so the nearest value that satisfies a step 34798 * is used. 34799 * 34800 * See the [HTML Spec on input[type=range]](https://www.w3.org/TR/html5/forms.html#range-state-(type=range)) 34801 * for more info. 34802 * 34803 * This has the following consequences for Angular: 34804 * 34805 * Since the element value should always reflect the current model value, a range input 34806 * will set the bound ngModel expression to the value that the browser has set for the 34807 * input element. For example, in the following input `<input type="range" ng-model="model.value">`, 34808 * if the application sets `model.value = null`, the browser will set the input to `'50'`. 34809 * Angular will then set the model to `50`, to prevent input and model value being out of sync. 34810 * 34811 * That means the model for range will immediately be set to `50` after `ngModel` has been 34812 * initialized. It also means a range input can never have the required error. 34813 * 34814 * This does not only affect changes to the model value, but also to the values of the `min`, 34815 * `max`, and `step` attributes. When these change in a way that will cause the browser to modify 34816 * the input value, Angular will also update the model value. 34817 * 34818 * Automatic value adjustment also means that a range input element can never have the `required`, 34819 * `min`, or `max` errors. 34820 * 34821 * However, `step` is currently only fully implemented by Firefox. Other browsers have problems 34822 * when the step value changes dynamically - they do not adjust the element value correctly, but 34823 * instead may set the `stepMismatch` error. If that's the case, the Angular will set the `step` 34824 * error on the input, and set the model to `undefined`. 34825 * 34826 * Note that `input[range]` is not compatible with`ngMax`, `ngMin`, and `ngStep`, because they do 34827 * not set the `min` and `max` attributes, which means that the browser won't automatically adjust 34828 * the input value based on their values, and will always assume min = 0, max = 100, and step = 1. 34829 * 34830 * @param {string} ngModel Assignable angular expression to data-bind to. 34831 * @param {string=} name Property name of the form under which the control is published. 34832 * @param {string=} min Sets the `min` validation to ensure that the value entered is greater 34833 * than `min`. Can be interpolated. 34834 * @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`. 34835 * Can be interpolated. 34836 * @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step` 34837 * Can be interpolated. 34838 * @param {string=} ngChange Angular expression to be executed when the ngModel value changes due 34839 *
vendor: 2,634 bytes, lines 34839-34905
34839 to user interaction with the input element. 34840 * @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the 34841 * element. **Note** : `ngChecked` should not be used alongside `ngModel`. 34842 * Checkout {@link ng.directive:ngChecked ngChecked} for usage. 34843 * 34844 * @example 34845 <example name="range-input-directive" module="rangeExample"> 34846 <file name="index.html"> 34847 <script> 34848 angular.module('rangeExample', []) 34849 .controller('ExampleController', ['$scope', function($scope) { 34850 $scope.value = 75; 34851 $scope.min = 10; 34852 $scope.max = 90; 34853 }]); 34854 </script> 34855 <form name="myForm" ng-controller="ExampleController"> 34856 34857 Model as range: <input type="range" name="range" ng-model="value" min="{{min}}" max="{{max}}"> 34858 <hr> 34859 Model as number: <input type="number" ng-model="value"><br> 34860 Min: <input type="number" ng-model="min"><br> 34861 Max: <input type="number" ng-model="max"><br> 34862 value = <code>{{value}}</code><br/> 34863 myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/> 34864 myForm.range.$error = <code>{{myForm.range.$error}}</code> 34865 </form> 34866 </file> 34867 </example> 34868 34869 * ## Range Input with ngMin & ngMax attributes 34870 34871 * @example 34872 <example name="range-input-directive-ng" module="rangeExample"> 34873 <file name="index.html"> 34874 <script> 34875 angular.module('rangeExample', []) 34876 .controller('ExampleController', ['$scope', function($scope) { 34877 $scope.value = 75; 34878 $scope.min = 10; 34879 $scope.max = 90; 34880 }]); 34881 </script> 34882 <form name="myForm" ng-controller="ExampleController"> 34883 Model as range: <input type="range" name="range" ng-model="value" ng-min="min" ng-max="max"> 34884 <hr> 34885 Model as number: <input type="number" ng-model="value"><br> 34886 Min: <input type="number" ng-model="min"><br> 34887 Max: <input type="number" ng-model="max"><br> 34888 value = <code>{{value}}</code><br/> 34889 myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/> 34890 myForm.range.$error = <code>{{myForm.range.$error}}</code> 34891 </form> 34892 </file> 34893 </example> 34894 34895 */ 34896 'range': rangeInputType, 34897 34898 /** 34899 * @ngdoc input 34900 * @name input[checkbox] 34901 * 34902 * @description 34903 * HTML checkbox. 34904 * 34905 * @param {string} ngModel Assignable
34905angular expression to data-bind to. 34906 * @param {string=} name Property name of the form under which the control is published. 34907 * @param {expression=} ngTrueValue The value to which the expression should be set when selected. 34908 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected. 34909 * @param {string=} ngChange Angular expression to be executed when input changes due to user 34910 * interaction with the input element. 34911 * 34912 * @example 34913 <example name="checkbox-input-directive" module="checkboxExample"> 34914 <file name="index.html"> 34915 <script> 34916 angular.module('checkboxExample', []) 34917 .controller('ExampleController', ['$scope', function($scope) {
34918 $scope.checkboxModel = { 34919 value1 : true, 34920 value2 : 'YES' 34921 }; 34922 }]); 34923 </script> 34924 <form name="myForm" ng-controller="ExampleController"> 34925 <label>Value1: 34926 <input type="checkbox" ng-model="checkboxModel.value1"> 34927 </label><br/> 34928 <label>Value2: 34929 <input type="checkbox" ng-model="checkboxModel.value2" 34930 ng-true-value="'YES'" ng-false-value="'NO'"> 34931 </label><br/> 34932 <tt>value1 = {{checkboxModel.value1}}</tt><br/> 34933 <tt>value2 = {{checkboxModel.value2}}</tt><br/> 34934 </form> 34935 </file> 34936 <file name="protractor.js" type="protractor"> 34937 it('should change state', function() { 34938 var value1 = element(by.binding('checkboxModel.value1')); 34939 var value2 = element(by.binding('checkboxModel.value2')); 34940 34941 expect(value1.getText()).toContain('true'); 34942 expect(value2.getText()).toContain('YES'); 34943 34944 element(by.model('checkboxModel.value1')).click(); 34945 element(by.model('checkboxModel.value2')).click(); 34946 34947 expect(value1.getText()).toContain('false'); 34948 expect(value2.getText()).toContain('NO'); 34949 }); 34950 </file> 34951 </example> 34952 */ 34953 'checkbox': checkboxInputType, 34954 34955 'hidden': noop, 34956 'button': noop, 34957 'submit': noop, 34958 'reset': noop, 34959 'file': noop 34960}; 34961 34962function stringBasedInputType(ctrl) { 34963 ctrl.$formatters.push(function(value) { 34964 return ctrl.$isEmpty(value) ? value : value.toString(); 34965 }); 34966} 34967 34968function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { 34969 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); 34970 stringBasedInputType(ctrl); 34971} 34972 34973function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { 34974 var type = lowercase(element[0].type); 34975 34976 // In composition mode, users are still inputting intermediate text buffer, 34977 // hold the listener until composition is done. 34978 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent 34979 if (!$sniffer.android) { 34980 var composing = false; 34981 34982 element.on('compositionstart', function() { 34983 composing = true; 34984 }); 34985 34986 element.on('compositionend', function() { 34987 composing = false; 34988 listener(); 34989 }); 34990 } 34991 34992 var timeout; 34993 34994 var listener = function(ev) { 34995 if (timeout) { 34996 $browser.defer.cancel(timeout); 34997 timeout = null; 34998 } 34999 if (composing) return; 35000 var value = element.val(), 35001 event = ev && ev.type; 35002 35003 // By default we will trim the value 35004 // If the attribute ng-trim exists we will avoid trimming 35005 // If input type is 'password', the value is never trimmed 35006 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { 35007 value = trim(value); 35008 } 35009 35010 // If a control is suffering from bad input (due to native validators), browsers discard its 35011 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the 35012 // control's value is the same empty value twice in a row. 35013 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) { 35014 ctrl.$setViewValue(value, event); 35015 } 35016 }; 35017 35018 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the 35019 // input event on backspace, delete or cut 35020 if ($sniffer.hasEvent('input')) { 35021 element.on('input', listener); 35022 } else { 35023 var deferListener = function(ev, input, origValue) { 35024 if (!timeout) { 35025 timeout = $browser.defer(function() { 35026 timeout = null; 35027 if (!input || input.value !== origValue) { 35028 listener(ev); 35029 } 35030 }); 35031 } 35032 }; 35033 35034 element.on('keydown', /** @this */ function(event) { 35035 var key = event.keyCode; 35036 35037 // ignore 35038 // command modifiers arrows 35039 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; 35040 35041 deferListener(event, this, this.value); 35042 }); 35043 35044 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it 35045 if ($sniffer.hasEvent('paste')) { 35046 element.on('paste cut', deferListener); 35047 } 35048 } 35049 35050 // if user paste into input using mouse on older browser 35051 // or form autocomplete on newer browser, we need "change" event to catch it 35052 element.on('change', listener); 35053 35054 // Some native input types (date-family) have the ability to change validity without 35055 // firing any input/change events. 35056 // For these event types, when native validators are present and the browser supports the type, 35057 // check for validity changes on various DOM events. 35058 if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) { 35059 element.on(PARTIAL_VALIDATION_EVENTS, /** @this */ function(ev) { 35060 if (!timeout) {
vendor: 17,297 bytes, lines 35061-35597
35061 var validity = this[VALIDITY_STATE_PROPERTY]; 35062 var origBadInput = validity.badInput; 35063 var origTypeMismatch = validity.typeMismatch; 35064 timeout = $browser.defer(function() { 35065 timeout = null; 35066 if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) { 35067 listener(ev); 35068 } 35069 }); 35070 } 35071 }); 35072 } 35073 35074 ctrl.$render = function() { 35075 // Workaround for Firefox validation #12102. 35076 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue; 35077 if (element.val() !== value) { 35078 element.val(value); 35079 } 35080 }; 35081} 35082 35083function weekParser(isoWeek, existingDate) { 35084 if (isDate(isoWeek)) { 35085 return isoWeek; 35086 } 35087 35088 if (isString(isoWeek)) { 35089 WEEK_REGEXP.lastIndex = 0; 35090 var parts = WEEK_REGEXP.exec(isoWeek); 35091 if (parts) { 35092 var year = +parts[1], 35093 week = +parts[2], 35094 hours = 0, 35095 minutes = 0, 35096 seconds = 0, 35097 milliseconds = 0, 35098 firstThurs = getFirstThursdayOfYear(year), 35099 addDays = (week - 1) * 7; 35100 35101 if (existingDate) { 35102 hours = existingDate.getHours(); 35103 minutes = existingDate.getMinutes(); 35104 seconds = existingDate.getSeconds(); 35105 milliseconds = existingDate.getMilliseconds(); 35106 } 35107 35108 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds); 35109 } 35110 } 35111 35112 return NaN; 35113} 35114 35115function createDateParser(regexp, mapping) { 35116 return function(iso, date) { 35117 var parts, map; 35118 35119 if (isDate(iso)) { 35120 return iso; 35121 } 35122 35123 if (isString(iso)) { 35124 // When a date is JSON'ified to wraps itself inside of an extra 35125 // set of double quotes. This makes the date parsing code unable 35126 // to match the date string and parse it as a date. 35127 if (iso.charAt(0) === '"' && iso.charAt(iso.length - 1) === '"') { 35128 iso = iso.substring(1, iso.length - 1); 35129 } 35130 if (ISO_DATE_REGEXP.test(iso)) { 35131 return new Date(iso); 35132 } 35133 regexp.lastIndex = 0; 35134 parts = regexp.exec(iso); 35135 35136 if (parts) { 35137 parts.shift(); 35138 if (date) { 35139 map = { 35140 yyyy: date.getFullYear(), 35141 MM: date.getMonth() + 1, 35142 dd: date.getDate(), 35143 HH: date.getHours(), 35144 mm: date.getMinutes(), 35145 ss: date.getSeconds(), 35146 sss: date.getMilliseconds() / 1000 35147 }; 35148 } else { 35149 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 }; 35150 } 35151 35152 forEach(parts, function(part, index) { 35153 if (index < mapping.length) { 35154 map[mapping[index]] = +part; 35155 } 35156 }); 35157 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0); 35158 } 35159 } 35160 35161 return NaN; 35162 }; 35163} 35164 35165function createDateInputType(type, regexp, parseDate, format) { 35166 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { 35167 badInputChecker(scope, element, attr, ctrl); 35168 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); 35169 var timezone = ctrl && ctrl.$options.getOption('timezone'); 35170 var previousDate; 35171 35172 ctrl.$$parserName = type; 35173 ctrl.$parsers.push(function(value) { 35174 if (ctrl.$isEmpty(value)) return null; 35175 if (regexp.test(value)) { 35176 // Note: We cannot read ctrl.$modelValue, as there might be a different 35177 // parser/formatter in the processing chain so that the model 35178 // contains some different data format! 35179 var parsedDate = parseDate(value, previousDate); 35180 if (timezone) { 35181 parsedDate = convertTimezoneToLocal(parsedDate, timezone); 35182 } 35183 return parsedDate; 35184 } 35185 return undefined; 35186 }); 35187 35188 ctrl.$formatters.push(function(value) { 35189 if (value && !isDate(value)) { 35190 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); 35191 } 35192 if (isValidDate(value)) { 35193 previousDate = value; 35194 if (previousDate && timezone) { 35195 previousDate = convertTimezoneToLocal(previousDate, timezone, true); 35196 } 35197 return $filter('date')(value, format, timezone); 35198 } else { 35199 previousDate = null; 35200 return ''; 35201 } 35202 }); 35203 35204 if (isDefined(attr.min) || attr.ngMin) { 35205 var minVal; 35206 ctrl.$validators.min = function(value) { 35207 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; 35208 }; 35209 attr.$observe('min', function(val) { 35210 minVal = parseObservedDateValue(val); 35211 ctrl.$validate(); 35212 }); 35213 } 35214 35215 if (isDefined(attr.max) || attr.ngMax) { 35216 var maxVal; 35217 ctrl.$validators.max = function(value) { 35218 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; 35219 }; 35220 attr.$observe('max', function(val) { 35221 maxVal = parseObservedDateValue(val); 35222 ctrl.$validate(); 35223 }); 35224 } 35225 35226 function isValidDate(value) { 35227 // Invalid Date: getTime() returns NaN 35228 return value && !(value.getTime && value.getTime() !== value.getTime()); 35229 } 35230 35231 function parseObservedDateValue(val) { 35232 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val; 35233 } 35234 }; 35235} 35236 35237function badInputChecker(scope, element, attr, ctrl) { 35238 var node = element[0]; 35239 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity); 35240 if (nativeValidation) { 35241 ctrl.$parsers.push(function(value) { 35242 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {}; 35243 return validity.badInput || validity.typeMismatch ? undefined : value; 35244 }); 35245 } 35246} 35247 35248function numberFormatterParser(ctrl) { 35249 ctrl.$$parserName = 'number'; 35250 ctrl.$parsers.push(function(value) { 35251 if (ctrl.$isEmpty(value)) return null; 35252 if (NUMBER_REGEXP.test(value)) return parseFloat(value); 35253 return undefined; 35254 }); 35255 35256 ctrl.$formatters.push(function(value) { 35257 if (!ctrl.$isEmpty(value)) { 35258 if (!isNumber(value)) { 35259 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); 35260 } 35261 value = value.toString(); 35262 } 35263 return value; 35264 }); 35265} 35266 35267function parseNumberAttrVal(val) { 35268 if (isDefined(val) && !isNumber(val)) { 35269 val = parseFloat(val); 35270 } 35271 return !isNumberNaN(val) ? val : undefined; 35272} 35273 35274function isNumberInteger(num) { 35275 // See http://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066 35276 // (minus the assumption that `num` is a number) 35277 35278 // eslint-disable-next-line no-bitwise 35279 return (num | 0) === num; 35280} 35281 35282function countDecimals(num) { 35283 var numString = num.toString(); 35284 var decimalSymbolIndex = numString.indexOf('.'); 35285 35286 if (decimalSymbolIndex === -1) { 35287 if (-1 < num && num < 1) { 35288 // It may be in the exponential notation format (`1e-X`) 35289 var match = /e-(\d+)$/.exec(numString); 35290 35291 if (match) { 35292 return Number(match[1]); 35293 } 35294 } 35295 35296 return 0; 35297 } 35298 35299 return numString.length - decimalSymbolIndex - 1; 35300} 35301 35302function isValidForStep(viewValue, stepBase, step) { 35303 // At this point `stepBase` and `step` are expected to be non-NaN values 35304 // and `viewValue` is expected to be a valid stringified number. 35305 var value = Number(viewValue); 35306 35307 var isNonIntegerValue = !isNumberInteger(value); 35308 var isNonIntegerStepBase = !isNumberInteger(stepBase); 35309 var isNonIntegerStep = !isNumberInteger(step); 35310 35311 // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or 35312 // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers. 35313 if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) { 35314 var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0; 35315 var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0; 35316 var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0; 35317 35318 var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals); 35319 var multiplier = Math.pow(10, decimalCount); 35320 35321 value = value * multiplier; 35322 stepBase = stepBase * multiplier; 35323 step = step * multiplier; 35324 35325 if (isNonIntegerValue) value = Math.round(value); 35326 if (isNonIntegerStepBase) stepBase = Math.round(stepBase); 35327 if (isNonIntegerStep) step = Math.round(step); 35328 } 35329 35330 return (value - stepBase) % step === 0; 35331} 35332 35333function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { 35334 badInputChecker(scope, element, attr, ctrl); 35335 numberFormatterParser(ctrl); 35336 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); 35337 35338 var minVal; 35339 var maxVal; 35340 35341 if (isDefined(attr.min) || attr.ngMin) { 35342 ctrl.$validators.min = function(value) { 35343 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal; 35344 }; 35345 35346 attr.$observe('min', function(val) { 35347 minVal = parseNumberAttrVal(val); 35348 // TODO(matsko): implement validateLater to reduce number of validations 35349 ctrl.$validate(); 35350 }); 35351 } 35352 35353 if (isDefined(attr.max) || attr.ngMax) { 35354 ctrl.$validators.max = function(value) { 35355 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal; 35356 }; 35357 35358 attr.$observe('max', function(val) { 35359 maxVal = parseNumberAttrVal(val); 35360 // TODO(matsko): implement validateLater to reduce number of validations 35361 ctrl.$validate(); 35362 }); 35363 } 35364 35365 if (isDefined(attr.step) || attr.ngStep) { 35366 var stepVal; 35367 ctrl.$validators.step = function(modelValue, viewValue) { 35368 return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) || 35369 isValidForStep(viewValue, minVal || 0, stepVal); 35370 }; 35371 35372 attr.$observe('step', function(val) { 35373 stepVal = parseNumberAttrVal(val); 35374 // TODO(matsko): implement validateLater to reduce number of validations 35375 ctrl.$validate(); 35376 }); 35377 } 35378} 35379 35380function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { 35381 badInputChecker(scope, element, attr, ctrl); 35382 numberFormatterParser(ctrl); 35383 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); 35384 35385 var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range', 35386 minVal = supportsRange ? 0 : undefined, 35387 maxVal = supportsRange ? 100 : undefined, 35388 stepVal = supportsRange ? 1 : undefined, 35389 validity = element[0].validity, 35390 hasMinAttr = isDefined(attr.min), 35391 hasMaxAttr = isDefined(attr.max), 35392 hasStepAttr = isDefined(attr.step); 35393 35394 var originalRender = ctrl.$render; 35395 35396 ctrl.$render = supportsRange && isDefined(validity.rangeUnderflow) && isDefined(validity.rangeOverflow) ? 35397 //Browsers that implement range will set these values automatically, but reading the adjusted values after 35398 //$render would cause the min / max validators to be applied with the wrong value 35399 function rangeRender() { 35400 originalRender(); 35401 ctrl.$setViewValue(element.val()); 35402 } : 35403 originalRender; 35404 35405 if (hasMinAttr) { 35406 ctrl.$validators.min = supportsRange ? 35407 // Since all browsers set the input to a valid value, we don't need to check validity 35408 function noopMinValidator() { return true; } : 35409 // non-support browsers validate the min val 35410 function minValidator(modelValue, viewValue) { 35411 return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal; 35412 }; 35413 35414 setInitialValueAndObserver('min', minChange); 35415 } 35416 35417 if (hasMaxAttr) { 35418 ctrl.$validators.max = supportsRange ? 35419 // Since all browsers set the input to a valid value, we don't need to check validity 35420 function noopMaxValidator() { return true; } : 35421 // non-support browsers validate the max val 35422 function maxValidator(modelValue, viewValue) { 35423 return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal; 35424 }; 35425 35426 setInitialValueAndObserver('max', maxChange); 35427 } 35428 35429 if (hasStepAttr) { 35430 ctrl.$validators.step = supportsRange ? 35431 function nativeStepValidator() { 35432 // Currently, only FF implements the spec on step change correctly (i.e. adjusting the 35433 // input element value to a valid value). It's possible that other browsers set the stepMismatch 35434 // validity error instead, so we can at least report an error in that case. 35435 return !validity.stepMismatch; 35436 } : 35437 // ngStep doesn't set the setp attr, so the browser doesn't adjust the input value as setting step would 35438 function stepValidator(modelValue, viewValue) { 35439 return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) || 35440 isValidForStep(viewValue, minVal || 0, stepVal); 35441 }; 35442 35443 setInitialValueAndObserver('step', stepChange); 35444 } 35445 35446 function setInitialValueAndObserver(htmlAttrName, changeFn) { 35447 // interpolated attributes set the attribute value only after a digest, but we need the 35448 // attribute value when the input is first rendered, so that the browser can adjust the 35449 // input value based on the min/max value 35450 element.attr(htmlAttrName, attr[htmlAttrName]); 35451 attr.$observe(htmlAttrName, changeFn); 35452 } 35453 35454 function minChange(val) { 35455 minVal = parseNumberAttrVal(val); 35456 // ignore changes before model is initialized 35457 if (isNumberNaN(ctrl.$modelValue)) { 35458 return; 35459 } 35460 35461 if (supportsRange) { 35462 var elVal = element.val(); 35463 // IE11 doesn't set the el val correctly if the minVal is greater than the element value 35464 if (minVal > elVal) { 35465 elVal = minVal; 35466 element.val(elVal); 35467 } 35468 ctrl.$setViewValue(elVal); 35469 } else { 35470 // TODO(matsko): implement validateLater to reduce number of validations 35471 ctrl.$validate(); 35472 } 35473 } 35474 35475 function maxChange(val) { 35476 maxVal = parseNumberAttrVal(val); 35477 // ignore changes before model is initialized 35478 if (isNumberNaN(ctrl.$modelValue)) { 35479 return; 35480 } 35481 35482 if (supportsRange) { 35483 var elVal = element.val(); 35484 // IE11 doesn't set the el val correctly if the maxVal is less than the element value 35485 if (maxVal < elVal) { 35486 element.val(maxVal); 35487 // IE11 and Chrome don't set the value to the minVal when max < min 35488 elVal = maxVal < minVal ? minVal : maxVal; 35489 } 35490 ctrl.$setViewValue(elVal); 35491 } else { 35492 // TODO(matsko): implement validateLater to reduce number of validations 35493 ctrl.$validate(); 35494 } 35495 } 35496 35497 function stepChange(val) { 35498 stepVal = parseNumberAttrVal(val); 35499 // ignore changes before model is initialized 35500 if (isNumberNaN(ctrl.$modelValue)) { 35501 return; 35502 } 35503 35504 // Some browsers don't adjust the input value correctly, but set the stepMismatch error 35505 if (supportsRange && ctrl.$viewValue !== element.val()) { 35506 ctrl.$setViewValue(element.val()); 35507 } else { 35508 // TODO(matsko): implement validateLater to reduce number of validations 35509 ctrl.$validate(); 35510 } 35511 } 35512} 35513 35514function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { 35515 // Note: no badInputChecker here by purpose as `url` is only a validation 35516 // in browsers, i.e. we can always read out input.value even if it is not valid! 35517 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); 35518 stringBasedInputType(ctrl); 35519 35520 ctrl.$$parserName = 'url'; 35521 ctrl.$validators.url = function(modelValue, viewValue) { 35522 var value = modelValue || viewValue; 35523 return ctrl.$isEmpty(value) || URL_REGEXP.test(value); 35524 }; 35525} 35526 35527function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { 35528 // Note: no badInputChecker here by purpose as `url` is only a validation 35529 // in browsers, i.e. we can always read out input.value even if it is not valid! 35530 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); 35531 stringBasedInputType(ctrl); 35532 35533 ctrl.$$parserName = 'email'; 35534 ctrl.$validators.email = function(modelValue, viewValue) { 35535 var value = modelValue || viewValue; 35536 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); 35537 }; 35538} 35539 35540function radioInputType(scope, element, attr, ctrl) { 35541 var doTrim = !attr.ngTrim || trim(attr.ngTrim) !== 'false'; 35542 // make the name unique, if not defined 35543 if (isUndefined(attr.name)) { 35544 element.attr('name', nextUid()); 35545 } 35546 35547 var listener = function(ev) { 35548 var value; 35549 if (element[0].checked) { 35550 value = attr.value; 35551 if (doTrim) { 35552 value = trim(value); 35553 } 35554 ctrl.$setViewValue(value, ev && ev.type); 35555 } 35556 }; 35557 35558 element.on('click', listener); 35559 35560 ctrl.$render = function() { 35561 var value = attr.value; 35562 if (doTrim) { 35563 value = trim(value); 35564 } 35565 element[0].checked = (value === ctrl.$viewValue); 35566 }; 35567 35568 attr.$observe('value', ctrl.$render); 35569} 35570 35571function parseConstantExpr($parse, context, name, expression, fallback) { 35572 var parseFn; 35573 if (isDefined(expression)) { 35574 parseFn = $parse(expression); 35575 if (!parseFn.constant) { 35576 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' + 35577 '`{1}`.', name, expression); 35578 } 35579 return parseFn(context); 35580 } 35581 return fallback; 35582} 35583 35584function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { 35585 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true); 35586 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false); 35587 35588 var listener = function(ev) { 35589 ctrl.$setViewValue(element[0].checked, ev && ev.type); 35590 }; 35591 35592 element.on('click', listener); 35593 35594 ctrl.$render = function() { 35595 element[0].checked = ctrl.$viewValue; 35596 }; 35597
35598 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` 35599 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert 35600 // it to a boolean. 35601 ctrl.$isEmpty = function(value) { 35602 return value === false; 35603 }; 35604 35605 ctrl.$formatters.push(function(value) { 35606 return equals(value, trueValue); 35607 }); 35608 35609 ctrl.$parsers.push(function(value) { 35610 return value ? trueValue : falseValue; 35611 }); 35612} 35613 35614 35615/** 35616 * @ngdoc directive 35617 * @name textarea 35618 * @restrict E 35619 * 35620 * @description 35621 * HTML textarea element control with angular data-binding. The data-binding and validation 35622 * properties of this element are exactly the same as those of the 35623 * {@link ng.directive:input input element}. 35624 * 35625 * @param {string} ngModel Assignable angular expression to data-bind to. 35626 * @param {string=} name Property name of the form under which the control is published. 35627 * @param {string=} required Sets `required` validation error key if the value is not entered. 35628 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 35629 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 35630 * `required` when you want to data-bind to the `required` attribute. 35631 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 35632 * minlength. 35633 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 35634 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any 35635 * length. 35636 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} 35637 * does not match a RegExp found by evaluating the Angular expression given in the attribute value. 35638 * If the expression evaluates to a RegExp object, then this is used directly. 35639 * If the expression evaluates to a string, then it will be converted to a RegExp 35640 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to 35641 * `new RegExp('^abc$')`.<br /> 35642 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to 35643 * start at the index of the last search's match, thus not taking the whole input value into 35644 * account. 35645 * @param {string=} ngChange Angular expression to be executed when input changes due to user 35646 * interaction with the input element. 35647 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. 35648 * 35649 * @knownIssue 35650 * 35651 * When specifying the `placeholder` attribute of `<textarea>`, Internet Explorer will temporarily 35652 * insert the placeholder value as the textarea's content. If the placeholder value contains 35653 * interpolation (`{{ ... }}`), an error will be logged in the console when Angular tries to update 35654 * the value of the by-then-removed text node. This doesn't affect the functionality of the 35655 * textarea, but can be undesirable. 35656 * 35657 * You can work around this Internet Explorer issue by using `ng-attr-placeholder` instead of 35658 * `placeholder` on textareas, whenever you need interpolation in the placeholder value. You can 35659 * find more details on `ngAttr` in the 35660 * [Interpolation](guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes) section of the 35661 * Developer Guide. 35662 */ 35663 35664 35665/** 35666 * @ngdoc directive 35667 * @name input 35668 * @restrict E 35669 * 35670 * @description 35671 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding, 35672 * input state control, and validation. 35673 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers. 35674 * 35675 * <div class="alert alert-warning"> 35676 * **Note:** Not every feature offered is available for all input types. 35677 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`. 35678 * </div> 35679 * 35680 * @param {string} ngModel Assignable angular expression to data-bind to. 35681 * @param {string=} name Property name of the form under which the control is published. 35682 * @param {string=} required Sets `required` validation error key if the value is not entered. 35683 * @param {boolean=} ngRequired Sets `required` attribute if set to true 35684 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 35685 * minlength. 35686 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 35687 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any 35688 * length. 35689 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} 35690 * value does not match a RegExp found by evaluating the Angular expression given in the attribute value. 35691 * If the expression evaluates to a RegExp object, then this is used directly. 35692 * If the expression evaluates to a string, then it will be converted to a RegExp 35693 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to 35694 * `new RegExp('^abc$')`.<br /> 35695 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to 35696 * start at the index of the last search's match, thus not taking the whole input value into 35697 * account. 35698 * @param {string=} ngChange Angular expression to be executed when input changes due to user 35699 * interaction with the input element. 35700 * @param {boolean=} [ngTrim=true] If set to false Angular
vendor: 4,842 bytes, lines 35700-35818
35700 will not automatically trim the input. 35701 * This parameter is ignored for input[type=password] controls, which will never trim the 35702 * input. 35703 * 35704 * @example 35705 <example name="input-directive" module="inputExample"> 35706 <file name="index.html"> 35707 <script> 35708 angular.module('inputExample', []) 35709 .controller('ExampleController', ['$scope', function($scope) { 35710 $scope.user = {name: 'guest', last: 'visitor'}; 35711 }]); 35712 </script> 35713 <div ng-controller="ExampleController"> 35714 <form name="myForm"> 35715 <label> 35716 User name: 35717 <input type="text" name="userName" ng-model="user.name" required> 35718 </label> 35719 <div role="alert"> 35720 <span class="error" ng-show="myForm.userName.$error.required"> 35721 Required!</span> 35722 </div> 35723 <label> 35724 Last name: 35725 <input type="text" name="lastName" ng-model="user.last" 35726 ng-minlength="3" ng-maxlength="10"> 35727 </label> 35728 <div role="alert"> 35729 <span class="error" ng-show="myForm.lastName.$error.minlength"> 35730 Too short!</span> 35731 <span class="error" ng-show="myForm.lastName.$error.maxlength"> 35732 Too long!</span> 35733 </div> 35734 </form> 35735 <hr> 35736 <tt>user = {{user}}</tt><br/> 35737 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/> 35738 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/> 35739 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/> 35740 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/> 35741 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 35742 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 35743 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/> 35744 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/> 35745 </div> 35746 </file> 35747 <file name="protractor.js" type="protractor"> 35748 var user = element(by.exactBinding('user')); 35749 var userNameValid = element(by.binding('myForm.userName.$valid')); 35750 var lastNameValid = element(by.binding('myForm.lastName.$valid')); 35751 var lastNameError = element(by.binding('myForm.lastName.$error')); 35752 var formValid = element(by.binding('myForm.$valid')); 35753 var userNameInput = element(by.model('user.name')); 35754 var userLastInput = element(by.model('user.last')); 35755 35756 it('should initialize to model', function() { 35757 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); 35758 expect(userNameValid.getText()).toContain('true'); 35759 expect(formValid.getText()).toContain('true'); 35760 }); 35761 35762 it('should be invalid if empty when required', function() { 35763 userNameInput.clear(); 35764 userNameInput.sendKeys(''); 35765 35766 expect(user.getText()).toContain('{"last":"visitor"}'); 35767 expect(userNameValid.getText()).toContain('false'); 35768 expect(formValid.getText()).toContain('false'); 35769 }); 35770 35771 it('should be valid if empty when min length is set', function() { 35772 userLastInput.clear(); 35773 userLastInput.sendKeys(''); 35774 35775 expect(user.getText()).toContain('{"name":"guest","last":""}'); 35776 expect(lastNameValid.getText()).toContain('true'); 35777 expect(formValid.getText()).toContain('true'); 35778 }); 35779 35780 it('should be invalid if less than required min length', function() { 35781 userLastInput.clear(); 35782 userLastInput.sendKeys('xx'); 35783 35784 expect(user.getText()).toContain('{"name":"guest"}'); 35785 expect(lastNameValid.getText()).toContain('false'); 35786 expect(lastNameError.getText()).toContain('minlength'); 35787 expect(formValid.getText()).toContain('false'); 35788 }); 35789 35790 it('should be invalid if longer than max length', function() { 35791 userLastInput.clear(); 35792 userLastInput.sendKeys('some ridiculously long name'); 35793 35794 expect(user.getText()).toContain('{"name":"guest"}'); 35795 expect(lastNameValid.getText()).toContain('false'); 35796 expect(lastNameError.getText()).toContain('maxlength'); 35797 expect(formValid.getText()).toContain('false'); 35798 }); 35799 </file> 35800 </example> 35801 */ 35802var inputDirective = ['$browser', '$sniffer', '$filter', '$parse', 35803 function($browser, $sniffer, $filter, $parse) { 35804 return { 35805 restrict: 'E', 35806 require: ['?ngModel'], 35807 link: { 35808 pre: function(scope, element, attr, ctrls) { 35809 if (ctrls[0]) { 35810 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer, 35811 $browser, $filter, $parse); 35812 } 35813 } 35814 } 35815 }; 35816}]; 35817 35818
35819 35820var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; 35821/** 35822 * @ngdoc directive 35823 * @name ngValue 35824 * 35825 * @description 35826 * Binds the given expression to the value of the element. 35827 * 35828 * It is mainly used on {@link input[radio] `input[radio]`} and option elements, 35829 * so that when the element is selected, the {@link ngModel `ngModel`} of that element (or its 35830 * {@link select `select`} parent element) is set to the bound value. It is especially useful 35831 * for dynamically generated lists using {@link ngRepeat `ngRepeat`}, as shown below. 35832 * 35833 * It can also be used to achieve one-way binding of a given expression to an input element 35834 * such as an `input[text]` or a `textarea`, when that element does not use ngModel. 35835 * 35836 * @element input 35837 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute 35838 * and `value` property of the element. 35839 * 35840 * @example 35841 <example name="ngValue-directive" module="valueExample"> 35842 <file name="index.html"> 35843 <script> 35844 angular.module('valueExample', []) 35845 .controller('ExampleController', ['$scope', function($scope) { 35846 $scope.names = ['pizza', 'unicorns', 'robots']; 35847 $scope.my = { favorite: 'unicorns' }; 35848 }]); 35849 </script>
35850 <form ng-controller="ExampleController"> 35851 <h2>Which is your favorite?</h2> 35852 <label ng-repeat="name in names" for="{{name}}"> 35853 {{name}} 35854 <input type="radio" 35855 ng-model="my.favorite" 35856 ng-value="name" 35857 id="{{name}}" 35858 name="favorite"> 35859 </label> 35860 <div>You chose {{my.favorite}}</div> 35861 </form> 35862 </file> 35863 <file name="protractor.js" type="protractor"> 35864 var favorite = element(by.binding('my.favorite')); 35865 35866 it('should initialize to model', function() { 35867 expect(favorite.getText()).toContain('unicorns'); 35868 }); 35869 it('should bind the values to the inputs', function() { 35870 element.all(by.model('my.favorite')).get(0).click(); 35871 expect(favorite.getText()).toContain('pizza'); 35872 }); 35873 </file> 35874 </example> 35875 */ 35876var ngValueDirective = function() { 35877 /** 35878 * inputs use the value attribute as their default value if the value property is not set. 35879 * Once the value property has been set (by adding input), it will not react to changes to 35880 * the value attribute anymore. Setting both attribute and property fixes this behavior, and 35881 * makes it possible to use ngValue as a sort of one-way bind. 35882 */ 35883 function updateElementValue(element, attr, value) { 35884 // Support: IE9 only 35885 // In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`). 35886 var propValue = isDefined(value) ? value : (msie === 9) ? '' : null; 35887 element.prop('value', propValue); 35888 attr.$set('value', value); 35889 } 35890 35891 return { 35892 restrict: 'A', 35893 priority: 100, 35894 compile: function(tpl, tplAttr) { 35895 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { 35896 return function ngValueConstantLink(scope, elm, attr) { 35897 var value = scope.$eval(attr.ngValue); 35898 updateElementValue(elm, attr, value); 35899 }; 35900 } else { 35901 return function ngValueLink(scope, elm, attr) { 35902 scope.$watch(attr.ngValue, function valueWatchAction(value) { 35903 updateElementValue(elm, attr, value); 35904 }); 35905 }; 35906 } 35907 } 35908 }; 35909}; 35910 35911/** 35912 * @ngdoc directive 35913 * @name ngBind 35914 * @restrict AC 35915 * 35916 * @description 35917 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element 35918 * with the value of a given expression, and to update the text content when the value of that 35919 * expression changes. 35920 * 35921 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like 35922 * `{{ expression }}` which is similar but less verbose. 35923 * 35924 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily 35925 * displayed by the browser in its raw state before Angular
vendor: 4,777 bytes, lines 35925-36055
35925 compiles it. Since `ngBind` is an 35926 * element attribute, it makes the bindings invisible to the user while the page is loading. 35927 * 35928 * An alternative solution to this problem would be using the 35929 * {@link ng.directive:ngCloak ngCloak} directive. 35930 * 35931 * 35932 * @element ANY 35933 * @param {expression} ngBind {@link guide/expression Expression} to evaluate. 35934 * 35935 * @example 35936 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. 35937 <example module="bindExample" name="ng-bind"> 35938 <file name="index.html"> 35939 <script> 35940 angular.module('bindExample', []) 35941 .controller('ExampleController', ['$scope', function($scope) { 35942 $scope.name = 'Whirled'; 35943 }]); 35944 </script> 35945 <div ng-controller="ExampleController"> 35946 <label>Enter name: <input type="text" ng-model="name"></label><br> 35947 Hello <span ng-bind="name"></span>! 35948 </div> 35949 </file> 35950 <file name="protractor.js" type="protractor"> 35951 it('should check ng-bind', function() { 35952 var nameInput = element(by.model('name')); 35953 35954 expect(element(by.binding('name')).getText()).toBe('Whirled'); 35955 nameInput.clear(); 35956 nameInput.sendKeys('world'); 35957 expect(element(by.binding('name')).getText()).toBe('world'); 35958 }); 35959 </file> 35960 </example> 35961 */ 35962var ngBindDirective = ['$compile', function($compile) { 35963 return { 35964 restrict: 'AC', 35965 compile: function ngBindCompile(templateElement) { 35966 $compile.$$addBindingClass(templateElement); 35967 return function ngBindLink(scope, element, attr) { 35968 $compile.$$addBindingInfo(element, attr.ngBind); 35969 element = element[0]; 35970 scope.$watch(attr.ngBind, function ngBindWatchAction(value) { 35971 element.textContent = stringify(value); 35972 }); 35973 }; 35974 } 35975 }; 35976}]; 35977 35978 35979/** 35980 * @ngdoc directive 35981 * @name ngBindTemplate 35982 * 35983 * @description 35984 * The `ngBindTemplate` directive specifies that the element 35985 * text content should be replaced with the interpolation of the template 35986 * in the `ngBindTemplate` attribute. 35987 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}` 35988 * expressions. This directive is needed since some HTML elements 35989 * (such as TITLE and OPTION) cannot contain SPAN elements. 35990 * 35991 * @element ANY 35992 * @param {string} ngBindTemplate template of form 35993 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval. 35994 * 35995 * @example 35996 * Try it here: enter text in text box and watch the greeting change. 35997 <example module="bindExample" name="ng-bind-template"> 35998 <file name="index.html"> 35999 <script> 36000 angular.module('bindExample', []) 36001 .controller('ExampleController', ['$scope', function($scope) { 36002 $scope.salutation = 'Hello'; 36003 $scope.name = 'World'; 36004 }]); 36005 </script> 36006 <div ng-controller="ExampleController"> 36007 <label>Salutation: <input type="text" ng-model="salutation"></label><br> 36008 <label>Name: <input type="text" ng-model="name"></label><br> 36009 <pre ng-bind-template="{{salutation}} {{name}}!"></pre> 36010 </div> 36011 </file> 36012 <file name="protractor.js" type="protractor"> 36013 it('should check ng-bind', function() { 36014 var salutationElem = element(by.binding('salutation')); 36015 var salutationInput = element(by.model('salutation')); 36016 var nameInput = element(by.model('name')); 36017 36018 expect(salutationElem.getText()).toBe('Hello World!'); 36019 36020 salutationInput.clear(); 36021 salutationInput.sendKeys('Greetings'); 36022 nameInput.clear(); 36023 nameInput.sendKeys('user'); 36024 36025 expect(salutationElem.getText()).toBe('Greetings user!'); 36026 }); 36027 </file> 36028 </example> 36029 */ 36030var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) { 36031 return { 36032 compile: function ngBindTemplateCompile(templateElement) { 36033 $compile.$$addBindingClass(templateElement); 36034 return function ngBindTemplateLink(scope, element, attr) { 36035 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); 36036 $compile.$$addBindingInfo(element, interpolateFn.expressions); 36037 element = element[0]; 36038 attr.$observe('ngBindTemplate', function(value) { 36039 element.textContent = isUndefined(value) ? '' : value; 36040 }); 36041 }; 36042 } 36043 }; 36044}]; 36045 36046 36047/** 36048 * @ngdoc directive 36049 * @name ngBindHtml 36050 * 36051 * @description 36052 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default, 36053 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service. 36054 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link 36055 * ngSanitize} in your module's dependencies (not in core Angular
36055). In order to use {@link ngSanitize} 36056 * in your module's dependencies, you need to include "angular-sanitize.js" in your application. 36057 * 36058 * You may also bypass sanitization for values you know are safe. To do so, bind to 36059 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example 36060 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}. 36061 * 36062 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you 36063 * will have an exception (instead of an exploit.) 36064 * 36065 * @element ANY 36066 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. 36067 * 36068 * @example 36069 36070 <example module="bindHtmlExample" deps="angular-sanitize.js" name="ng-bind-html"> 36071 <file name="index.html"> 36072 <div ng-controller="ExampleController"> 36073 <p ng-bind-html="myHTML"></p> 36074 </div> 36075 </file> 36076 36077 <file name="script.js"> 36078 angular.module('bindHtmlExample', ['ngSanitize']) 36079 .controller('ExampleController', ['$scope', function($scope) { 36080 $scope.myHTML = 36081 'I am an <code>HTML</code>string with ' + 36082 '<a href="#">links!</a> and other <em>stuff</em>'; 36083 }]); 36084 </file> 36085 36086 <file name="protractor.js" type="protractor"> 36087 it('should check ng-bind-html', function() { 36088 expect(element(by.binding('myHTML')).getText()).toBe( 36089 'I am an HTMLstring with links! and other stuff'); 36090 }); 36091 </file> 36092 </example> 36093 */ 36094var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) { 36095 return { 36096 restrict: 'A',
36097 compile: function ngBindHtmlCompile(tElement, tAttrs) { 36098 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml); 36099 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) { 36100 // Unwrap the value to compare the actual inner safe value, not the wrapper object. 36101 return $sce.valueOf(val); 36102 }); 36103 $compile.$$addBindingClass(tElement); 36104 36105 return function ngBindHtmlLink(scope, element, attr) { 36106 $compile.$$addBindingInfo(element, attr.ngBindHtml); 36107 36108 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() { 36109 // The watched value is the unwrapped value. To avoid re-escaping, use the direct getter. 36110 var value = ngBindHtmlGetter(scope); 36111 element.html($sce.getTrustedHtml(value) || ''); 36112 }); 36113 }; 36114 } 36115 }; 36116}]; 36117 36118/** 36119 * @ngdoc directive 36120 * @name ngChange 36121 * 36122 * @description 36123 * Evaluate the given expression when the user changes the input. 36124 * The expression is evaluated immediately, unlike the JavaScript onchange event 36125 * which only triggers at the end of a change (usually, when the user leaves the 36126 * form element or presses the return key). 36127 * 36128 * The `ngChange` expression is only evaluated when a change in the input value causes 36129 * a new value to be committed to the model. 36130 * 36131 * It will not be evaluated: 36132 * * if the value returned from the `$parsers` transformation pipeline has not changed 36133 * * if the input has continued to be invalid since the model will stay `null` 36134 * * if the model is changed programmatically and not by a change to the input value 36135 * 36136 * 36137 * Note, this directive requires `ngModel` to be present. 36138 * 36139 * @element input 36140 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change 36141 * in input value. 36142 * 36143 * @example 36144 * <example name="ngChange-directive" module="changeExample"> 36145 * <file name="index.html"> 36146 * <script> 36147 * angular.module('changeExample', []) 36148 * .controller('ExampleController', ['$scope', function($scope) { 36149 * $scope.counter = 0; 36150 * $scope.change = function() { 36151 * $scope.counter++; 36152 * }; 36153 * }]); 36154 * </script>
36155 * <div ng-controller="ExampleController"> 36156 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" /> 36157 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" /> 36158 * <label for="ng-change-example2">Confirmed</label><br /> 36159 * <tt>debug = {{confirmed}}</tt><br/> 36160 * <tt>counter = {{counter}}</tt><br/> 36161 * </div> 36162 * </file> 36163 * <file name="protractor.js" type="protractor"> 36164 * var counter = element(by.binding('counter')); 36165 * var debug = element(by.binding('confirmed')); 36166 * 36167 * it('should evaluate the expression if changing from view', function() { 36168 * expect(counter.getText()).toContain('0'); 36169 * 36170 * element(by.id('ng-change-example1')).click(); 36171 * 36172 * expect(counter.getText()).toContain('1'); 36173 * expect(debug.getText()).toContain('true'); 36174 * }); 36175 * 36176 * it('should not evaluate the expression if changing from model', function() { 36177 * element(by.id('ng-change-example2')).click(); 36178 36179 * expect(counter.getText()).toContain('0'); 36180 * expect(debug.getText()).toContain('true'); 36181 * }); 36182 * </file> 36183 * </example> 36184 */ 36185var ngChangeDirective = valueFn({ 36186 restrict: 'A', 36187 require: 'ngModel', 36188 link: function(scope, element, attr, ctrl) { 36189 ctrl.$viewChangeListeners.push(function() { 36190 scope.$eval(attr.ngChange); 36191 }); 36192 } 36193}); 36194 36195/* exported 36196 ngClassDirective, 36197 ngClassEvenDirective, 36198 ngClassOddDirective 36199*/ 36200 36201function classDirective(name, selector) { 36202 name = 'ngClass' + name; 36203 var indexWatchExpression; 36204 36205 return ['$parse', function($parse) { 36206 return { 36207 restrict: 'AC', 36208 link: function(scope, element, attr) { 36209 var expression = attr[name].trim(); 36210 var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':'); 36211 36212 var watchInterceptor = isOneTime ? toFlatValue : toClassString; 36213 var watchExpression = $parse(expression, watchInterceptor); 36214 var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction; 36215 36216 var classCounts = element.data('$classCounts'); 36217 var oldModulo = true; 36218 var oldClassString; 36219 36220 if (!classCounts) { 36221 // Use createMap() to prevent class assumptions involving property 36222 // names in Object.prototype 36223 classCounts = createMap(); 36224 element.data('$classCounts', classCounts); 36225 } 36226 36227 if (name !== 'ngClass') { 36228 if (!indexWatchExpression) { 36229 indexWatchExpression = $parse('$index', function moduloTwo($index) { 36230 // eslint-disable-next-line no-bitwise 36231 return $index & 1; 36232 }); 36233 } 36234 36235 scope.$watch(indexWatchExpression, ngClassIndexWatchAction); 36236 } 36237 36238 scope.$watch(watchExpression, watchAction, isOneTime); 36239 36240 function addClasses(classString) { 36241 classString = digestClassCounts(split(classString), 1); 36242 attr.$addClass(classString); 36243 } 36244 36245 function removeClasses(classString) { 36246 classString = digestClassCounts(split(classString), -1); 36247 attr.$removeClass(classString); 36248 } 36249 36250 function updateClasses(oldClassString, newClassString) { 36251 var oldClassArray = split(oldClassString); 36252 var newClassArray = split(newClassString); 36253 36254 var toRemoveArray = arrayDifference(oldClassArray, newClassArray); 36255 var toAddArray = arrayDifference(newClassArray, oldClassArray); 36256 36257 var toRemoveString = digestClassCounts(toRemoveArray, -1); 36258 var toAddString = digestClassCounts(toAddArray, 1); 36259 36260 attr.$addClass(toAddString); 36261 attr.$removeClass(toRemoveString); 36262 } 36263 36264 function digestClassCounts(classArray, count) { 36265 var classesToUpdate = []; 36266 36267 forEach(classArray, function(className) { 36268 if (count > 0 || classCounts[className]) { 36269 classCounts[className] = (classCounts[className] || 0) + count; 36270 if (classCounts[className] === +(count > 0)) { 36271 classesToUpdate.push(className); 36272 } 36273 } 36274 }); 36275 36276 return classesToUpdate.join(' '); 36277 } 36278 36279 function ngClassIndexWatchAction(newModulo) { 36280 // This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it 36281 // adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the 36282 // `ngClass[OneTime]WatchAction()` will update the classes. 36283 if (newModulo === selector) { 36284 addClasses(oldClassString); 36285 } else { 36286 removeClasses(oldClassString); 36287 } 36288 36289 oldModulo = newModulo; 36290 } 36291 36292 function ngClassOneTimeWatchAction(newClassValue) { 36293 var newClassString = toClassString(newClassValue); 36294 36295 if (newClassString !== oldClassString) { 36296 ngClassWatchAction(newClassString); 36297 } 36298 } 36299 36300 function ngClassWatchAction(newClassString) { 36301 if (oldModulo === selector) { 36302 updateClasses(oldClassString, newClassString); 36303 } 36304 36305 oldClassString = newClassString; 36306 } 36307 } 36308 }; 36309 }]; 36310 36311 // Helpers 36312 function arrayDifference(tokens1, tokens2) { 36313 if (!tokens1 || !tokens1.length) return []; 36314 if (!tokens2 || !tokens2.length) return tokens1; 36315 36316 var values = []; 36317 36318 outer: 36319 for (var i = 0; i < tokens1.length; i++) { 36320 var token = tokens1[i]; 36321 for (var j = 0; j < tokens2.length; j++) { 36322 if (token === tokens2[j]) continue outer; 36323 } 36324 values.push(token); 36325 } 36326 36327 return values; 36328 } 36329 36330 function split(classString) { 36331 return classString && classString.split(' '); 36332 } 36333 36334 function toClassString(classValue) { 36335 var classString = classValue; 36336 36337 if (isArray(classValue)) { 36338 classString = classValue.map(toClassString).join(' '); 36339 } else if (isObject(classValue)) { 36340 classString = Object.keys(classValue). 36341 filter(function(key) { return classValue[key]; }). 36342 join(' '); 36343 } 36344 36345 return classString; 36346 } 36347
36348 function toFlatValue(classValue) { 36349 var flatValue = classValue; 36350 36351 if (isArray(classValue)) { 36352 flatValue = classValue.map(toFlatValue); 36353 } else if (isObject(classValue)) { 36354 var hasUndefined = false; 36355 36356 flatValue = Object.keys(classValue).filter(function(key) { 36357 var value = classValue[key]; 36358 36359 if (!hasUndefined && isUndefined(value)) { 36360 hasUndefined = true; 36361 } 36362 36363 return value; 36364 }); 36365 36366 if (hasUndefined) { 36367 // Prevent the `oneTimeLiteralWatchInterceptor` from unregistering 36368 // the watcher, by including at least one `undefined` value. 36369 flatValue.push(undefined); 36370 } 36371 } 36372 36373 return flatValue; 36374 } 36375} 36376 36377/** 36378 * @ngdoc directive 36379 * @name ngClass 36380 * @restrict AC 36381 * 36382 * @description 36383 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding 36384 * an expression that represents all classes to be added. 36385 * 36386 * The directive operates in three different ways, depending on which of three types the expression 36387 * evaluates to: 36388 * 36389 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class 36390 * names. 36391 * 36392 * 2. If the expression evaluates to an object, then for each key-value pair of the 36393 * object with a truthy value the corresponding key is used as a class name. 36394 * 36395 * 3. If the expression evaluates to an array, each element of the array should either be a string as in 36396 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array 36397 * to give you more control over what CSS classes appear. See the code below for an example of this. 36398 * 36399 * 36400 * The directive won't add duplicate classes if a particular class was already set. 36401 * 36402 * When the expression changes, the previously added classes are removed and only then are the 36403 * new classes added. 36404 * 36405 * @knownIssue 36406 * You should not use {@link guide/interpolation interpolation} in the value of the `class` 36407 * attribute, when using the `ngClass` directive on the same element. 36408 * See {@link guide/interpolation#known-issues here} for more info. 36409 * 36410 * @animations 36411 * | Animation | Occurs | 36412 * |----------------------------------|-------------------------------------| 36413 * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element | 36414 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element | 36415 * 36416 * @element ANY 36417 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result 36418 * of the evaluation can be a string representing space delimited class 36419 * names, an array, or a map of class names to boolean values. In the case of a map, the 36420 * names of the properties whose values are truthy will be added as css classes to the 36421 * element. 36422 * 36423 * @example Example that demonstrates basic bindings via ngClass directive. 36424 <example name="ng-class"> 36425 <file name="index.html"> 36426 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p> 36427 <label> 36428 <input type="checkbox" ng-model="deleted"> 36429 deleted (apply "strike" class) 36430 </label><br> 36431 <label> 36432 <input type="checkbox" ng-model="important"> 36433 important (apply "bold" class) 36434 </label><br> 36435 <label> 36436 <input type="checkbox" ng-model="error"> 36437 error (apply "has-error" class) 36438 </label> 36439 <hr> 36440 <p ng-class="style">Using String Syntax</p> 36441 <input type="text" ng-model="style" 36442 placeholder="Type: bold strike red" aria-label="Type: bold strike red"> 36443 <hr> 36444 <p ng-class="[style1, style2, style3]">Using Array Syntax</p> 36445 <input ng-model="style1" 36446 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br> 36447 <input ng-model="style2" 36448 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br> 36449 <input ng-model="style3" 36450 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br> 36451 <hr> 36452 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p> 36453 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br> 36454 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label> 36455 </file> 36456 <file name="style.css"> 36457 .strike { 36458 text-decoration: line-through; 36459 } 36460 .bold { 36461 font-weight: bold; 36462 } 36463 .red { 36464 color: red; 36465 } 36466 .has-error { 36467 color: red; 36468 background-color: yellow; 36469 } 36470 .orange { 36471 color: orange; 36472 } 36473 </file> 36474 <file name="protractor.js" type="protractor"> 36475 var ps = element.all(by.css('p')); 36476 36477 it('should let you toggle the class', function() { 36478 36479 expect(ps.first().getAttribute('class')).not.toMatch(/bold/); 36480 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/); 36481
36482 element(by.model('important')).click(); 36483 expect(ps.first().getAttribute('class')).toMatch(/bold/); 36484 36485 element(by.model('error')).click(); 36486 expect(ps.first().getAttribute('class')).toMatch(/has-error/); 36487 }); 36488 36489 it('should let you toggle string example', function() { 36490 expect(ps.get(1).getAttribute('class')).toBe(''); 36491 element(by.model('style')).clear(); 36492 element(by.model('style')).sendKeys('red'); 36493 expect(ps.get(1).getAttribute('class')).toBe('red'); 36494 }); 36495 36496 it('array example should have 3 classes', function() { 36497 expect(ps.get(2).getAttribute('class')).toBe(''); 36498 element(by.model('style1')).sendKeys('bold'); 36499 element(by.model('style2')).sendKeys('strike'); 36500 element(by.model('style3')).sendKeys('red'); 36501 expect(ps.get(2).getAttribute('class')).toBe('bold strike red'); 36502 }); 36503 36504 it('array with map example should have 2 classes', function() { 36505 expect(ps.last().getAttribute('class')).toBe(''); 36506 element(by.model('style4')).sendKeys('bold'); 36507 element(by.model('warning')).click(); 36508 expect(ps.last().getAttribute('class')).toBe('bold orange'); 36509 }); 36510 </file> 36511 </example> 36512 36513 ## Animations 36514 36515 The example below demonstrates how to perform animations using ngClass. 36516 36517 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-class"> 36518 <file name="index.html"> 36519 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'"> 36520 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''"> 36521 <br> 36522 <span class="base-class" ng-class="myVar">Sample Text</span> 36523 </file> 36524 <file name="style.css"> 36525 .base-class { 36526 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 36527 } 36528 36529 .base-class.my-class { 36530 color: red; 36531 font-size:3em; 36532 } 36533 </file> 36534 <file name="protractor.js" type="protractor"> 36535 it('should check ng-class', function() { 36536 expect(element(by.css('.base-class')).getAttribute('class')).not. 36537 toMatch(/my-class/); 36538 36539 element(by.id('setbtn')).click(); 36540 36541 expect(element(by.css('.base-class')).getAttribute('class')). 36542 toMatch(/my-class/); 36543 36544 element(by.id('clearbtn')).click(); 36545 36546 expect(element(by.css('.base-class')).getAttribute('class')).not. 36547 toMatch(/my-class/); 36548 }); 36549 </file> 36550 </example> 36551 36552 36553 ## ngClass and pre-existing CSS3 Transitions/Animations 36554 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. 36555 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder 36556 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure 36557 to view the step by step details of {@link $animate#addClass $animate.addClass} and 36558 {@link $animate#removeClass $animate.removeClass}. 36559 */ 36560var ngClassDirective = classDirective('', true); 36561 36562/** 36563 * @ngdoc directive 36564 * @name ngClassOdd 36565 * @restrict AC 36566 * 36567 * @description 36568 * The `ngClassOdd` and `ngClassEven` directives work exactly as 36569 * {@link ng.directive:ngClass ngClass}, except they work in 36570 * conjunction with `ngRepeat` and take effect only on odd (even) rows. 36571 * 36572 * This directive can be applied only within the scope of an 36573 * {@link ng.directive:ngRepeat ngRepeat}. 36574 * 36575 * @element ANY 36576 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result 36577 * of the evaluation can be a string representing space delimited class names or an array. 36578 * 36579 * @example 36580 <example name="ng-class-odd"> 36581 <file name="index.html"> 36582 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> 36583 <li ng-repeat="name in names"> 36584 <span ng-class-odd="'odd'" ng-class-even="'even'"> 36585 {{name}} 36586 </span> 36587 </li> 36588 </ol> 36589 </file> 36590 <file name="style.css"> 36591 .odd { 36592 color: red; 36593 } 36594 .even { 36595 color: blue; 36596 } 36597 </file> 36598 <file name="protractor.js" type="protractor"> 36599 it('should check ng-class-odd and ng-class-even', function() { 36600 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). 36601 toMatch(/odd/); 36602 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). 36603 toMatch(/even/); 36604 }); 36605 </file> 36606 </example> 36607 */ 36608var ngClassOddDirective = classDirective('Odd', 0); 36609 36610/** 36611 * @ngdoc directive 36612 * @name ngClassEven 36613 * @restrict AC 36614 * 36615 * @description 36616 * The `ngClassOdd` and `ngClassEven` directives work exactly as 36617 * {@link ng.directive:ngClass ngClass}, except they work in 36618 * conjunction with `ngRepeat` and take effect only on odd (even) rows. 36619 * 36620 * This directive can be applied only within the scope of an 36621 * {@link ng.directive:ngRepeat ngRepeat}. 36622 * 36623 * @element ANY 36624 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The 36625 * result of the evaluation can be a string representing space delimited class names or an array.
36626 * 36627 * @example 36628 <example name="ng-class-even"> 36629 <file name="index.html"> 36630 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> 36631 <li ng-repeat="name in names"> 36632 <span ng-class-odd="'odd'" ng-class-even="'even'"> 36633 {{name}} 36634 </span> 36635 </li> 36636 </ol> 36637 </file> 36638 <file name="style.css"> 36639 .odd { 36640 color: red; 36641 } 36642 .even { 36643 color: blue; 36644 } 36645 </file> 36646 <file name="protractor.js" type="protractor"> 36647 it('should check ng-class-odd and ng-class-even', function() { 36648 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). 36649 toMatch(/odd/); 36650 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). 36651 toMatch(/even/); 36652 }); 36653 </file> 36654 </example> 36655 */ 36656var ngClassEvenDirective = classDirective('Even', 1); 36657 36658/** 36659 * @ngdoc directive 36660 * @name ngCloak 36661 * @restrict AC 36662 * 36663 * @description 36664 * The `ngCloak` directive is used to prevent the Angular html template from being briefly 36665 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this 36666 * directive to avoid the undesirable flicker effect caused by the html template display. 36667 * 36668 * The directive can be applied to the `<body>` element, but the preferred usage is to apply 36669 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering 36670 * of the browser view. 36671 * 36672 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and 36673 * `angular.min.js`. 36674 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). 36675 * 36676 * ```css 36677 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { 36678 * display: none !important; 36679 * } 36680 * ``` 36681 * 36682 * When this css rule is loaded by the browser, all html elements (including their children) that 36683 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive 36684 * during the compilation of the template it deletes the `ngCloak` element attribute, making 36685 * the compiled element visible. 36686 * 36687 * For the best result, the `angular.js` script must be loaded in the head section of the html 36688 * document; alternatively, the css rule above must be included in the external stylesheet of the 36689 * application. 36690 * 36691 * @element ANY 36692 * 36693 * @example 36694 <example name="ng-cloak"> 36695 <file name="index.html"> 36696 <div id="template1" ng-cloak>{{ 'hello' }}</div> 36697 <div id="template2" class="ng-cloak">{{ 'world' }}</div> 36698 </file> 36699 <file name="protractor.js" type="protractor"> 36700 it('should remove the template directive and css class', function() { 36701 expect($('#template1').getAttribute('ng-cloak')). 36702 toBeNull(); 36703 expect($('#template2').getAttribute('ng-cloak')). 36704 toBeNull(); 36705 }); 36706 </file> 36707 </example> 36708 * 36709 */ 36710var ngCloakDirective = ngDirective({ 36711 compile: function(element, attr) { 36712 attr.$set('ngCloak', undefined); 36713 element.removeClass('ng-cloak'); 36714 } 36715}); 36716 36717/** 36718 * @ngdoc directive 36719 * @name ngController 36720 * 36721 * @description 36722 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular 36723 * supports the principles behind the Model-View-Controller design pattern. 36724 * 36725 * MVC components in angular: 36726 * 36727 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties 36728 * are accessed through bindings. 36729 * * View — The template (HTML with data bindings) that is rendered into the View. 36730 * * Controller — The `ngController` directive specifies a Controller class; the class contains business 36731 * logic behind the application to decorate the scope with functions and values 36732 * 36733 * Note that you can also attach controllers to the DOM by declaring it in a route definition 36734 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller 36735 * again using `ng-controller` in the template itself. This will cause the controller to be attached 36736 * and executed twice. 36737 * 36738 * @element ANY 36739 * @scope 36740 * @priority 500 36741 * @param {expression} ngController Name of a constructor function registered with the current 36742 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression} 36743 * that on the current scope evaluates to a constructor function. 36744 * 36745 * The controller instance can be published into a scope property by specifying 36746 * `ng-controller="as propertyName"`. 36747 * 36748 * If the current `$controllerProvider` is configured to use globals (via 36749 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may 36750 * also be the name of a globally accessible constructor function (deprecated, not recommended). 36751 * 36752 * @example 36753 * Here is a simple form for editing user contact information. Adding, remov
36753ing, clearing, and 36754 * greeting are methods declared on the controller (see source tab). These methods can 36755 * easily be called from the angular markup. Any changes to the data are automatically reflected 36756 * in the View without the need for a manual update. 36757 * 36758 * Two different declaration styles are included below: 36759 * 36760 * * one binds methods and properties directly onto the controller using `this`: 36761 * `ng-controller="SettingsController1 as settings"` 36762 * * one injects `$scope` into the controller: 36763 * `ng-controller="SettingsController2"` 36764 * 36765 * The second option is more common in the Angular community, and is generally used in boilerplates 36766 * and in this guide. However, there are advantages to binding properties directly to the controller 36767 * and avoiding scope. 36768 * 36769 * * Using `controller as` makes it obvious which controller you are accessing in the template when 36770 * multiple controllers apply to an element. 36771 * * If you are writing your controllers as classes you have easier access to the properties and 36772 * methods, which will appear on the scope, from inside the controller code. 36773 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal 36774 * inheritance masking primitives. 36775 * 36776 * This example demonstrates the `controller as` syntax. 36777 * 36778 * <example name="ngControllerAs" module="controllerAsExample"> 36779 * <file name="index.html"> 36780 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings"> 36781 * <label>Name: <input type="text" ng-model="settings.name"/></label> 36782 * <button ng-click="settings.greet()">greet</button><br/> 36783 * Contact: 36784 * <ul> 36785 * <li ng-repeat="contact in settings.contacts"> 36786 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}"> 36787 * <option>phone</option> 36788 * <option>email</option> 36789 * </select> 36790 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" /> 36791 * <button ng-click="settings.clearContact(contact)">clear</button> 36792 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button> 36793 * </li> 36794 * <li><button ng-click="settings.addContact()">add</button></li> 36795 * </ul> 36796 * </div> 36797 * </file> 36798 * <file name="app.js"> 36799 * angular.module('controllerAsExample', []) 36800 * .controller('SettingsController1', SettingsController1); 36801 * 36802 * function SettingsController1() { 36803 * this.name = 'John Smith'; 36804 * this.contacts = [ 36805 * {type: 'phone', value: '408 555 1212'}, 36806 * {type: 'email', value: '[email protected]'} 36807 * ]; 36808 * } 36809 * 36810 * SettingsController1.prototype.greet = function() { 36811 * alert(this.name); 36812 * }; 36813 * 36814 * SettingsController1.prototype.addContact = function() { 36815 * this.contacts.push({type: 'email', value: '[email protected]'}); 36816 * }; 36817 * 36818 * SettingsController1.prototype.removeContact = function(contactToRemove) { 36819 * var index = this.contacts.indexOf(contactToRemove); 36820 * this.contacts.splice(index, 1); 36821 * }; 36822 * 36823 * SettingsController1.prototype.clearContact = function(contact) { 36824 * contact.type = 'phone'; 36825 * contact.value = ''; 36826 * }; 36827 * </file> 36828 * <file name="protractor.js" type="protractor"> 36829 * it('should check controller as', function() { 36830 * var container = element(by.id('ctrl-as-exmpl')); 36831 * expect(container.element(by.model('settings.name')) 36832 * .getAttribute('value')).toBe('John Smith'); 36833 * 36834 * var firstRepeat = 36835 * container.element(by.repeater('contact in settings.contacts').row(0)); 36836 * var secondRepeat = 36837 * container.element(by.repeater('contact in settings.contacts').row(1)); 36838 * 36839 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 36840 * .toBe('408 555 1212'); 36841 * 36842 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) 36843 * .toBe('[email protected]'); 36844 * 36845 * firstRepeat.element(by.buttonText('clear')).click(); 36846 * 36847 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 36848 * .toBe(''); 36849 * 36850 * container.element(by.buttonText('add')).click(); 36851 * 36852 * expect(container.element(by.repeater('contact in settings.contacts').row(2)) 36853 * .element(by.model('contact.value')) 36854 * .getAttribute('value')) 36855 * .toBe('[email protected]'); 36856 * }); 36857 * </file> 36858 * </example> 36859 * 36860 * This example demonstrates the "attach to `$scope`" style of controller. 36861 * 36862 * <example name="ngController" module="controllerExample"> 36863 * <file name="index.html"> 36864 * <div id="ctrl-exmpl" ng-controller="SettingsController2"> 36865 * <label>Name: <input type="text" ng-model="name"/></label> 36866 * <button ng-click="greet()">greet</button><br/> 36867 * Contact: 36868 * <ul> 36869 * <li ng-repeat="contact in contacts"> 36870 * <select ng-model="contact.type" id="select_{{$index}}"> 36871 * <option>phone</option> 36872 * <option>email</option> 36873 * </select> 36874 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" /> 36875 * <button ng-click="clearContact(contact)">clear</button> 36876 * <button ng-click="removeContact(contact)">X</button> 36877 * </li> 36878 * <li>[ <button ng-click="addContact()">add</button> ]</li> 36879 * </ul> 36880 * </div> 36881 * </file> 36882 * <file name="app.js">
36883 * angular.module('controllerExample', []) 36884 * .controller('SettingsController2', ['$scope', SettingsController2]); 36885 * 36886 * function SettingsController2($scope) { 36887 * $scope.name = 'John Smith'; 36888 * $scope.contacts = [ 36889 * {type:'phone', value:'408 555 1212'}, 36890 * {type:'email', value:'[email protected]'} 36891 * ]; 36892 * 36893 * $scope.greet = function() { 36894 * alert($scope.name); 36895 * }; 36896 * 36897 * $scope.addContact = function() { 36898 * $scope.contacts.push({type:'email', value:'[email protected]'}); 36899 * }; 36900 * 36901 * $scope.removeContact = function(contactToRemove) { 36902 * var index = $scope.contacts.indexOf(contactToRemove); 36903 * $scope.contacts.splice(index, 1); 36904 * }; 36905 * 36906 * $scope.clearContact = function(contact) { 36907 * contact.type = 'phone'; 36908 * contact.value = ''; 36909 * }; 36910 * } 36911 * </file> 36912 * <file name="protractor.js" type="protractor"> 36913 * it('should check controller', function() { 36914 * var container = element(by.id('ctrl-exmpl')); 36915 * 36916 * expect(container.element(by.model('name')) 36917 * .getAttribute('value')).toBe('John Smith'); 36918 * 36919 * var firstRepeat = 36920 * container.element(by.repeater('contact in contacts').row(0)); 36921 * var secondRepeat = 36922 * container.element(by.repeater('contact in contacts').row(1)); 36923 * 36924 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 36925 * .toBe('408 555 1212'); 36926 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) 36927 * .toBe('[email protected]'); 36928 * 36929 * firstRepeat.element(by.buttonText('clear')).click(); 36930 * 36931 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 36932 * .toBe(''); 36933 * 36934 * container.element(by.buttonText('add')).click(); 36935 * 36936 * expect(container.element(by.repeater('contact in contacts').row(2)) 36937 * .element(by.model('contact.value')) 36938 * .getAttribute('value')) 36939 * .toBe('[email protected]'); 36940 * }); 36941 * </file> 36942 *</example> 36943 36944 */ 36945var ngControllerDirective = [function() { 36946 return { 36947 restrict: 'A', 36948 scope: true, 36949 controller: '@', 36950 priority: 500 36951 }; 36952}]; 36953 36954/** 36955 * @ngdoc directive 36956 * @name ngCsp 36957 * 36958 * @restrict A 36959 * @element ANY 36960 * @description 36961 * 36962 * Angular has some features that can conflict with certain restrictions that are applied when using 36963 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules. 36964 * 36965 * If you intend to implement CSP with these rules then you must tell Angular not to use these 36966 * features. 36967 * 36968 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps. 36969 * 36970 * 36971 * The following default rules in CSP affect Angular: 36972 * 36973 * * The use of `eval()`, `Function(string)` and similar functions to dynamically create and execute 36974 * code from strings is forbidden. Angular makes use of this in the {@link $parse} service to 36975 * provide a 30% increase in the speed of evaluating Angular expressions. (This CSP rule can be 36976 * disabled with the CSP keyword `unsafe-eval`, but it is generally not recommended as it would 36977 * weaken the protections offered by CSP.) 36978 * 36979 * * The use of inline resources, such as inline `<script>` and `<style>` elements, are forbidden. 36980 * This prevents apps from injecting custom styles directly into the document. Angular makes use of 36981 * this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). To make these 36982 * directives work when a CSP rule is blocking inline styles, you must link to the `angular-csp.css` 36983 * in your HTML manually. (This CSP rule can be disabled with the CSP keyword `unsafe-inline`, but 36984 * it is generally not recommended as it would weaken the protections offered by CSP.) 36985 * 36986 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking dynamic code 36987 * creation from strings (e.g., `unsafe-eval` not specified in CSP header) and automatically 36988 * deactivates this feature in the {@link $parse} service. This autodetection, however, triggers a 36989 * CSP error to be logged in the console: 36990 * 36991 * ``` 36992 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of 36993 * script in the following Content Security Policy directive: "default-src 'self'". Note that 36994 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback. 36995 * ``` 36996 * 36997 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` 36998 * directive on an element of the HTML document that appears before the `<script>` tag that loads 36999 * the `angular.js` file. 37000 * 37001 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.* 37002 * 37003 * You can specify which of the CSP related Angular features should be deactivated by providing 37004 * a value for the `ng-csp` attribute. The options are as follows: 37005 * 37006 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM 37007 *
37008 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings 37009 * 37010 * You can use these values in the following combinations: 37011 * 37012 * 37013 * * No declaration means that Angular will assume that you can do inline styles, but it will do 37014 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous 37015 * versions of Angular. 37016 * 37017 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline 37018 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous 37019 * versions of Angular. 37020 * 37021 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can 37022 * inject inline styles. E.g. `<body ng-csp="no-unsafe-eval">`. 37023 * 37024 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can 37025 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">` 37026 * 37027 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject 37028 * styles nor use eval, which is the same as an empty: ng-csp. 37029 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">` 37030 * 37031 * @example 37032 * This example shows how to apply the `ngCsp` directive to the `html` tag. 37033 ```html 37034 <!doctype html> 37035 <html ng-app ng-csp> 37036 ... 37037 ... 37038 </html> 37039 ``` 37040 * @example 37041 <!-- Note: the `.csp` suffix in the example name triggers CSP mode in our http server! --> 37042 <example name="example.csp" module="cspExample" ng-csp="true"> 37043 <file name="index.html"> 37044 <div ng-controller="MainController as ctrl"> 37045 <div> 37046 <button ng-click="ctrl.inc()" id="inc">Increment</button> 37047 <span id="counter"> 37048 {{ctrl.counter}} 37049 </span> 37050 </div> 37051 37052 <div> 37053 <button ng-click="ctrl.evil()" id="evil">Evil</button> 37054 <span id="evilError"> 37055 {{ctrl.evilError}} 37056 </span> 37057 </div> 37058 </div> 37059 </file> 37060 <file name="script.js"> 37061 angular.module('cspExample', []) 37062 .controller('MainController', function MainController() { 37063 this.counter = 0; 37064 this.inc = function() { 37065 this.counter++; 37066 }; 37067 this.evil = function() { 37068 try { 37069 eval('1+2'); // eslint-disable-line no-eval 37070 } catch (e) { 37071 this.evilError = e.message; 37072 } 37073 }; 37074 }); 37075 </file> 37076 <file name="protractor.js" type="protractor"> 37077 var util, webdriver; 37078 37079 var incBtn = element(by.id('inc')); 37080 var counter = element(by.id('counter')); 37081 var evilBtn = element(by.id('evil')); 37082 var evilError = element(by.id('evilError')); 37083 37084 function getAndClearSevereErrors() { 37085 return browser.manage().logs().get('browser').then(function(browserLog) { 37086 return browserLog.filter(function(logEntry) { 37087 return logEntry.level.value > webdriver.logging.Level.WARNING.value; 37088 }); 37089 }); 37090 } 37091 37092 function clearErrors() { 37093 getAndClearSevereErrors(); 37094 } 37095 37096 function expectNoErrors() { 37097 getAndClearSevereErrors().then(function(filteredLog) { 37098 expect(filteredLog.length).toEqual(0); 37099 if (filteredLog.length) { 37100 console.log('browser console errors: ' + util.inspect(filteredLog)); 37101 } 37102 }); 37103 } 37104 37105 function expectError(regex) { 37106 getAndClearSevereErrors().then(function(filteredLog) { 37107 var found = false; 37108 filteredLog.forEach(function(log) { 37109 if (log.message.match(regex)) { 37110 found = true; 37111 } 37112 }); 37113 if (!found) { 37114 throw new Error('expected an error that matches ' + regex); 37115 } 37116 }); 37117 } 37118 37119 beforeEach(function() { 37120 util = require('util'); 37121 webdriver = require('selenium-webdriver'); 37122 }); 37123 37124 // For now, we only test on Chrome, 37125 // as Safari does not load the page with Protractor's injected scripts, 37126 // and Firefox webdriver always disables content security policy (#6358) 37127 if (browser.params.browser !== 'chrome') { 37128 return; 37129 } 37130 37131 it('should not report errors when the page is loaded', function() { 37132 // clear errors so we are not dependent on previous tests 37133 clearErrors(); 37134 // Need to reload the page as the page is already loaded when 37135 // we come here 37136 browser.driver.getCurrentUrl().then(function(url) { 37137 browser.get(url); 37138 }); 37139 expectNoErrors(); 37140 }); 37141 37142 it('should evaluate expressions', function() { 37143 expect(counter.getText()).toEqual('0'); 37144 incBtn.click(); 37145 expect(counter.getText()).toEqual('1'); 37146 expectNoErrors(); 37147 }); 37148 37149 it('should throw and report an error when using "eval"', function() { 37150 evilBtn.click(); 37151 expect(evilError.getText()).toMatch(/Content Security Policy/); 37152 expectError(/Content Security Policy/); 37153 }); 37154 </file> 37155 </example> 37156 */ 37157 37158// `ngCsp` is not implemented as a proper directive any more, because we need it be processed while 37159// we bootstrap the app (before `$parse` is instantiated). For this reason, we just have the `csp()` 37160// fn that looks for the `ng-csp` attribute anywhere in the current doc. 37161 37162/** 37163 * @ngdoc directive 37164 * @name ngClick 37165 * 37166 * @description 37167 * The ngClick directive allows you to specify custom behavior when 37168 * an element is clicked. 37169 * 37170 * @element ANY 37171 * @priority 0 37172 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon 37173 * click. ({@link guide/expression#-event- Event object is available as `$event`}) 37174 * 37175 * @example 37176 <example name="ng-click"> 37177 <file name="index.html"> 37178 <button ng-click="count = count + 1" ng-init="count=0"> 37179 Increment 37180 </button>
37181 <span> 37182 count: {{count}} 37183 </span> 37184 </file> 37185 <file name="protractor.js" type="protractor"> 37186 it('should check ng-click', function() { 37187 expect(element(by.binding('count')).getText()).toMatch('0'); 37188 element(by.css('button')).click(); 37189 expect(element(by.binding('count')).getText()).toMatch('1'); 37190 }); 37191 </file> 37192 </example> 37193 */ 37194/* 37195 * A collection of directives that allows creation of custom event handlers that are defined as 37196 * angular expressions and are compiled and executed within the current scope. 37197 */ 37198var ngEventDirectives = {}; 37199 37200// For events that might fire synchronously during DOM manipulation 37201// we need to execute their event handlers asynchronously using $evalAsync, 37202// so that they are not executed in an inconsistent state. 37203var forceAsyncEvents = { 37204 'blur': true, 37205 'focus': true 37206}; 37207forEach( 37208 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), 37209 function(eventName) { 37210 var directiveName = directiveNormalize('ng-' + eventName); 37211 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { 37212 return { 37213 restrict: 'A', 37214 compile: function($element, attr) { 37215 // NOTE: 37216 // We expose the powerful `$event` object on the scope that provides access to the Window, 37217 // etc. This is OK, because expressions are not sandboxed any more (and the expression 37218 // sandbox was never meant to be a security feature anyway). 37219 var fn = $parse(attr[directiveName]); 37220 return function ngEventHandler(scope, element) { 37221 element.on(eventName, function(event) { 37222 var callback = function() { 37223 fn(scope, {$event: event}); 37224 }; 37225 if (forceAsyncEvents[eventName] && $rootScope.$$phase) { 37226 scope.$evalAsync(callback); 37227 } else { 37228 scope.$apply(callback); 37229 } 37230 }); 37231 }; 37232 } 37233 }; 37234 }]; 37235 } 37236); 37237 37238/** 37239 * @ngdoc directive 37240 * @name ngDblclick 37241 * 37242 * @description 37243 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event. 37244 * 37245 * @element ANY 37246 * @priority 0 37247 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon 37248 * a dblclick. (The Event object is available as `$event`) 37249 * 37250 * @example 37251 <example name="ng-dblclick"> 37252 <file name="index.html"> 37253 <button ng-dblclick="count = count + 1" ng-init="count=0"> 37254 Increment (on double click) 37255 </button> 37256 count: {{count}} 37257 </file> 37258 </example> 37259 */ 37260 37261 37262/** 37263 * @ngdoc directive 37264 * @name ngMousedown 37265 * 37266 * @description 37267 * The ngMousedown directive allows you to specify custom behavior on mousedown event. 37268 * 37269 * @element ANY 37270 * @priority 0 37271 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon 37272 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`}) 37273 * 37274 * @example 37275 <example name="ng-mousedown"> 37276 <file name="index.html"> 37277 <button ng-mousedown="count = count + 1" ng-init="count=0"> 37278 Increment (on mouse down) 37279 </button> 37280 count: {{count}} 37281 </file> 37282 </example> 37283 */ 37284 37285 37286/** 37287 * @ngdoc directive 37288 * @name ngMouseup 37289 * 37290 * @description 37291 * Specify custom behavior on mouseup event. 37292 * 37293 * @element ANY 37294 * @priority 0 37295 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon 37296 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`}) 37297 * 37298 * @example 37299 <example name="ng-mouseup"> 37300 <file name="index.html"> 37301 <button ng-mouseup="count = count + 1" ng-init="count=0"> 37302 Increment (on mouse up) 37303 </button> 37304 count: {{count}} 37305 </file> 37306 </example> 37307 */ 37308 37309/** 37310 * @ngdoc directive 37311 * @name ngMouseover 37312 * 37313 * @description 37314 * Specify custom behavior on mouseover event. 37315 * 37316 * @element ANY 37317 * @priority 0 37318 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon 37319 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`}) 37320 * 37321 * @example 37322 <example name="ng-mouseover"> 37323 <file name="index.html"> 37324 <button ng-mouseover="count = count + 1" ng-init="count=0"> 37325 Increment (when mouse is over) 37326 </button> 37327 count: {{count}} 37328 </file> 37329 </example> 37330 */ 37331 37332 37333/** 37334 * @ngdoc directive 37335 * @name ngMouseenter 37336 * 37337 * @description 37338 * Specify custom behavior on mouseenter event. 37339 * 37340 * @element ANY 37341 * @priority 0 37342 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon 37343 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`}) 37344 * 37345 * @example 37346 <example name="ng-mouseenter"> 37347 <file name="index.html"> 37348 <button ng-mouseenter="count = count + 1" ng-init="count=0"> 37349 Increment (when mouse enters) 37350 </button> 37351 count: {{count}} 37352 </file> 37353 </example> 37354 */ 37355 37356 37357/** 37358 * @ngdoc directive 37359 * @name ngMouseleave 37360 * 37361 * @description 37362 * Specify custom behavior on mouseleave event. 37363 * 37364 * @element ANY 37365 * @priority 0 37366 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon 37367 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`}) 37368 * 37369 * @example 37370 <example name="ng-mouseleave"> 37371 <file name="index.html"> 37372 <button ng-mouseleave="count = count + 1" ng-init="count=0"> 37373 Increment (when mouse leaves) 37374 </button> 37375 count: {{count}} 37376 </file> 37377 </example> 37378 */ 37379 37380 37381/** 37382 * @ngdoc directive 37383 * @name ngMousemove 37384 * 37385 * @description 37386 * Specify custom behavior on mousemove event. 37387 * 37388 * @element ANY 37389 * @priority 0 37390 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon 37391 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`}) 37392 * 37393 * @example 37394 <example name="ng-mousemove"> 37395 <file name="index.html"> 37396 <button ng-mousemove="count = count + 1" ng-init="count=0"> 37397 Increment (when mouse moves) 37398 </button> 37399 count: {{count}} 37400 </file> 37401 </example> 37402 */ 37403 37404 37405/** 37406 * @ngdoc directive 37407 * @name ngKeydown 37408 * 37409 * @description 37410 * Specify custom behavior on keydown event. 37411 * 37412 * @element ANY 37413 * @priority 0 37414 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon 37415 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) 37416 * 37417 * @example 37418 <example name="ng-keydown"> 37419 <file name="index.html"> 37420 <input ng-keydown="count = count + 1" ng-init="count=0"> 37421 key down count: {{count}} 37422 </file> 37423 </example> 37424 */ 37425 37426 37427/** 37428 * @ngdoc directive 37429 * @name ngKeyup 37430 * 37431 * @description 37432 * Specify custom behavior on keyup event. 37433 * 37434 * @element ANY 37435 * @priority 0 37436 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon 37437 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) 37438 * 37439 * @example 37440 <example name="ng-keyup"> 37441 <file name="index.html"> 37442 <p>Typing in the input box below updates the key count</p> 37443 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}} 37444 37445 <p>Typing in the input box below updates the keycode</p> 37446 <input ng-keyup="event=$event"> 37447 <p>event keyCode: {{ event.keyCode }}</p> 37448 <p>event altKey: {{ event.altKey }}</p> 37449 </file> 37450 </example> 37451 */ 37452 37453 37454/** 37455 * @ngdoc directive 37456 * @name ngKeypress 37457 * 37458 * @description 37459 * Specify custom behavior on keypress event. 37460 * 37461 * @element ANY 37462 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon 37463 * keypress. ({@link guide/expression#-event- Event object is available as `$event`} 37464 * and can be interrogated for keyCode, altKey, etc.) 37465 * 37466 * @example 37467 <example name="ng-keypress"> 37468 <file name="index.html"> 37469 <input ng-keypress="count = count + 1" ng-init="count=0"> 37470 key press count: {{count}} 37471 </file> 37472 </example> 37473 */ 37474 37475 37476/** 37477 * @ngdoc directive 37478 * @name ngSubmit 37479 * 37480 * @description 37481 * Enables binding angular expressions to onsubmit events. 37482 * 37483 * Additionally it prevents the default action (which for form means
37483sending the request to the 37484 * server and reloading the current page), but only if the form does not contain `action`, 37485 * `data-action`, or `x-action` attributes. 37486 * 37487 * <div class="alert alert-warning"> 37488 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and 37489 * `ngSubmit` handlers together. See the 37490 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation} 37491 * for a detailed discussion of when `ngSubmit` may be triggered. 37492 * </div> 37493 * 37494 * @element form 37495 * @priority 0 37496 * @param {expression} ngSubmit {@link guide/expression Expression} to eval. 37497 * ({@link guide/expression#-event- Event object is available as `$event`}) 37498 * 37499 * @example 37500 <example module="submitExample" name="ng-submit"> 37501 <file name="index.html"> 37502 <script> 37503 angular.module('submitExample', []) 37504 .controller('ExampleController', ['$scope', function($scope) { 37505 $scope.list = []; 37506 $scope.text = 'hello'; 37507 $scope.submit = function() { 37508 if ($scope.text) { 37509 $scope.list.push(this.text); 37510 $scope.text = ''; 37511 } 37512 }; 37513 }]); 37514 </script>
37515 <form ng-submit="submit()" ng-controller="ExampleController"> 37516 Enter text and hit enter: 37517 <input type="text" ng-model="text" name="text" /> 37518 <input type="submit" id="submit" value="Submit" /> 37519 <pre>list={{list}}</pre> 37520 </form> 37521 </file> 37522 <file name="protractor.js" type="protractor"> 37523 it('should check ng-submit', function() { 37524 expect(element(by.binding('list')).getText()).toBe('list=[]'); 37525 element(by.css('#submit')).click(); 37526 expect(element(by.binding('list')).getText()).toContain('hello'); 37527 expect(element(by.model('text')).getAttribute('value')).toBe(''); 37528 }); 37529 it('should ignore empty strings', function() { 37530 expect(element(by.binding('list')).getText()).toBe('list=[]'); 37531 element(by.css('#submit')).click(); 37532 element(by.css('#submit')).click(); 37533 expect(element(by.binding('list')).getText()).toContain('hello'); 37534 }); 37535 </file> 37536 </example> 37537 */ 37538 37539/** 37540 * @ngdoc directive 37541 * @name ngFocus 37542 * 37543 * @description 37544 * Specify custom behavior on focus event. 37545 * 37546 * Note: As the `focus` event is executed synchronously when calling `input.focus()` 37547 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired 37548 * during an `$apply` to ensure a consistent state. 37549 * 37550 * @element window, input, select, textarea, a 37551 * @priority 0 37552 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon 37553 * focus. ({@link guide/expression#-event- Event object is available as `$event`}) 37554 * 37555 * @example 37556 * See {@link ng.directive:ngClick ngClick} 37557 */ 37558 37559/** 37560 * @ngdoc directive 37561 * @name ngBlur 37562 * 37563 * @description 37564 * Specify custom behavior on blur event. 37565 * 37566 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when 37567 * an element has lost focus. 37568 * 37569 * Note: As the `blur` event is executed synchronously also during DOM manipulations 37570 * (e.g. removing a focussed input), 37571 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired 37572 * during an `$apply` to ensure a consistent state. 37573 * 37574 * @element window, input, select, textarea, a 37575 * @priority 0 37576 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon 37577 * blur. ({@link guide/expression#-event- Event object is available as `$event`}) 37578 * 37579 * @example 37580 * See {@link ng.directive:ngClick ngClick} 37581 */ 37582 37583/** 37584 * @ngdoc directive 37585 * @name ngCopy 37586 * 37587 * @description 37588 * Specify custom behavior on copy event. 37589 * 37590 * @element window, input, select, textarea, a 37591 * @priority 0 37592 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon 37593 * copy. ({@link guide/expression#-event- Event object is available as `$event`}) 37594 * 37595 * @example 37596 <example name="ng-copy"> 37597 <file name="index.html"> 37598 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value"> 37599 copied: {{copied}} 37600 </file> 37601 </example> 37602 */ 37603 37604/** 37605 * @ngdoc directive 37606 * @name ngCut 37607 * 37608 * @description 37609 * Specify custom behavior on cut event. 37610 * 37611 * @element window, input, select, textarea, a 37612 * @priority 0 37613 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon 37614 * cut. ({@link guide/expression#-event- Event object is available as `$event`}) 37615 * 37616 * @example 37617 <example name="ng-cut"> 37618 <file name="index.html"> 37619 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value"> 37620 cut: {{cut}} 37621 </file> 37622 </example> 37623 */ 37624 37625/** 37626 * @ngdoc directive 37627 * @name ngPaste 37628 * 37629 * @description 37630 * Specify custom behavior on paste event. 37631 * 37632 * @element window, input, select, textarea, a 37633 * @priority 0 37634 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon 37635 * paste. ({@link guide/expression#-event- Event object is available as `$event`}) 37636 * 37637 * @example 37638 <example name="ng-paste"> 37639 <file name="index.html"> 37640 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'> 37641 pasted: {{paste}} 37642 </file> 37643 </example> 37644 */ 37645 37646/** 37647 * @ngdoc directive 37648 * @name ngIf 37649 * @restrict A 37650 * @multiElement 37651 * 37652 * @description 37653 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an 37654 * {expression}. If the expression assigned to `ngIf` evaluates to a false 37655 * value then the element is removed from the DOM, otherwise a clone of the 37656 * element is reinserted into the DOM. 37657 * 37658 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the 37659 * element in the DOM rather than changing its visibility via the `display` css property. A common 37660 * case when this difference is significant is when using css selectors that rely on an element's 37661 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
37661s. 37662 * 37663 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope 37664 * is created when the element is restored. The scope created within `ngIf` inherits from 37665 * its parent scope using 37666 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance). 37667 * An important implication of this is if `ngModel` is used within `ngIf` to bind to 37668 * a javascript primitive defined in the parent scope. In this case any modifications made to the 37669 * variable within the child scope will override (hide) the value in the parent scope. 37670 * 37671 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior 37672 * is if an element's class attribute is directly modified after it's compiled, using something like 37673 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element 37674 * the added class will be lost because the original compiled state is used to regenerate the element. 37675 * 37676 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter` 37677 * and `leave` effects. 37678 * 37679 * @animations 37680 * | Animation | Occurs | 37681 * |----------------------------------|-------------------------------------| 37682 * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container | 37683 * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM | 37684 * 37685 * @element ANY 37686 * @scope 37687 * @priority 600 37688 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then 37689 * the element is removed from the DOM tree. If it is truthy a copy of the compiled 37690 * element is added to the DOM tree. 37691 * 37692 * @example 37693 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-if"> 37694 <file name="index.html"> 37695 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/> 37696 Show when checked: 37697 <span ng-if="checked" class="animate-if"> 37698 This is removed when the checkbox is unchecked. 37699 </span> 37700 </file> 37701 <file name="animations.css"> 37702 .animate-if { 37703 background:white; 37704 border:1px solid black; 37705 padding:10px; 37706 } 37707 37708 .animate-if.ng-enter, .animate-if.ng-leave { 37709 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 37710 } 37711 37712 .animate-if.ng-enter, 37713 .animate-if.ng-leave.ng-leave-active { 37714 opacity:0; 37715 } 37716 37717 .animate-if.ng-leave, 37718 .animate-if.ng-enter.ng-enter-active { 37719 opacity:1; 37720 } 37721 </file> 37722 </example> 37723 */ 37724var ngIfDirective = ['$animate', '$compile', function($animate, $compile) { 37725 return { 37726 multiElement: true, 37727 transclude: 'element', 37728 priority: 600, 37729 terminal: true, 37730 restrict: 'A', 37731 $$tlb: true, 37732 link: function($scope, $element, $attr, ctrl, $transclude) { 37733 var block, childScope, previousElements; 37734 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { 37735 37736 if (value) { 37737 if (!childScope) { 37738 $transclude(function(clone, newScope) { 37739 childScope = newScope; 37740 clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf); 37741 // Note: We only need the first/last node of the cloned nodes. 37742 // However, we need to keep the reference to the jqlite wrapper as it might be changed later 37743 // by a directive with templateUrl when its template arrives. 37744 block = { 37745 clone: clone 37746 }; 37747 $animate.enter(clone, $element.parent(), $element); 37748 }); 37749 } 37750 } else { 37751 if (previousElements) { 37752 previousElements.remove(); 37753 previousElements = null; 37754 } 37755 if (childScope) { 37756 childScope.$destroy(); 37757 childScope = null; 37758 } 37759 if (block) { 37760 previousElements = getBlockNodes(block.clone); 37761 $animate.leave(previousElements).done(function(response) { 37762 if (response !== false) previousElements = null; 37763 }); 37764 block = null; 37765 } 37766 } 37767 }); 37768 } 37769 }; 37770}]; 37771 37772/** 37773 * @ngdoc directive 37774 * @name ngInclude 37775 * @restrict ECA 37776 * 37777 * @description 37778 * Fetches, compiles and includes an external HTML fragment. 37779 * 37780 * By default, the template URL is restricted to the same domain and protocol as the 37781 * application document. This is done by calling {@link $sce#getTrustedResourceUrl 37782 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols 37783 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or 37784 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link 37785 * ng.$sce Strict Contextual Escaping}. 37786 * 37787 * In addition, the browser's 37788 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) 37789 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) 37790 * policy may further restrict whether the template is successfully loaded. 37791 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://` 37792 * access on some browsers. 37793 * 37794 * @animations 37795 * | Animation | Occurs | 37796 * |----------------------------------|-------------------------------------| 37797 * | {@link ng.$animate#enter enter} | when the expression changes, on the new include | 37798 * | {@link ng.$animate#leave leave} | when the expression changes, on the old include | 37799 * 37800 * The enter and leave animation occur concurrently. 37801 * 37802 * @scope 37803 * @priority 400 37804 * 37805 * @param {string} ngInclude|src angular
vendor: 9,710 bytes, lines 37805-38082
37805 expression evaluating to URL. If the source is a string constant, 37806 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`. 37807 * @param {string=} onload Expression to evaluate when a new partial is loaded. 37808 * <div class="alert alert-warning"> 37809 * **Note:** When using onload on SVG elements in IE11, the browser will try to call 37810 * a function with the name on the window element, which will usually throw a 37811 * "function is undefined" error. To fix this, you can instead use `data-onload` or a 37812 * different form that {@link guide/directive#normalization matches} `onload`. 37813 * </div> 37814 * 37815 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll 37816 * $anchorScroll} to scroll the viewport after the content is loaded. 37817 * 37818 * - If the attribute is not set, disable scrolling. 37819 * - If the attribute is set without value, enable scrolling. 37820 * - Otherwise enable scrolling only if the expression evaluates to truthy value. 37821 * 37822 * @example 37823 <example module="includeExample" deps="angular-animate.js" animations="true" name="ng-include"> 37824 <file name="index.html"> 37825 <div ng-controller="ExampleController"> 37826 <select ng-model="template" ng-options="t.name for t in templates"> 37827 <option value="">(blank)</option> 37828 </select> 37829 url of the template: <code>{{template.url}}</code> 37830 <hr/> 37831 <div class="slide-animate-container"> 37832 <div class="slide-animate" ng-include="template.url"></div> 37833 </div> 37834 </div> 37835 </file> 37836 <file name="script.js"> 37837 angular.module('includeExample', ['ngAnimate']) 37838 .controller('ExampleController', ['$scope', function($scope) { 37839 $scope.templates = 37840 [{ name: 'template1.html', url: 'template1.html'}, 37841 { name: 'template2.html', url: 'template2.html'}]; 37842 $scope.template = $scope.templates[0]; 37843 }]); 37844 </file> 37845 <file name="template1.html"> 37846 Content of template1.html 37847 </file> 37848 <file name="template2.html"> 37849 Content of template2.html 37850 </file> 37851 <file name="animations.css"> 37852 .slide-animate-container { 37853 position:relative; 37854 background:white; 37855 border:1px solid black; 37856 height:40px; 37857 overflow:hidden; 37858 } 37859 37860 .slide-animate { 37861 padding:10px; 37862 } 37863 37864 .slide-animate.ng-enter, .slide-animate.ng-leave { 37865 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 37866 37867 position:absolute; 37868 top:0; 37869 left:0; 37870 right:0; 37871 bottom:0; 37872 display:block; 37873 padding:10px; 37874 } 37875 37876 .slide-animate.ng-enter { 37877 top:-50px; 37878 } 37879 .slide-animate.ng-enter.ng-enter-active { 37880 top:0; 37881 } 37882 37883 .slide-animate.ng-leave { 37884 top:0; 37885 } 37886 .slide-animate.ng-leave.ng-leave-active { 37887 top:50px; 37888 } 37889 </file> 37890 <file name="protractor.js" type="protractor"> 37891 var templateSelect = element(by.model('template')); 37892 var includeElem = element(by.css('[ng-include]')); 37893 37894 it('should load template1.html', function() { 37895 expect(includeElem.getText()).toMatch(/Content of template1.html/); 37896 }); 37897 37898 it('should load template2.html', function() { 37899 if (browser.params.browser === 'firefox') { 37900 // Firefox can't handle using selects 37901 // See https://github.com/angular/protractor/issues/480 37902 return; 37903 } 37904 templateSelect.click(); 37905 templateSelect.all(by.css('option')).get(2).click(); 37906 expect(includeElem.getText()).toMatch(/Content of template2.html/); 37907 }); 37908 37909 it('should change to blank', function() { 37910 if (browser.params.browser === 'firefox') { 37911 // Firefox can't handle using selects 37912 return; 37913 } 37914 templateSelect.click(); 37915 templateSelect.all(by.css('option')).get(0).click(); 37916 expect(includeElem.isPresent()).toBe(false); 37917 }); 37918 </file> 37919 </example> 37920 */ 37921 37922 37923/** 37924 * @ngdoc event 37925 * @name ngInclude#$includeContentRequested 37926 * @eventType emit on the scope ngInclude was declared in 37927 * @description 37928 * Emitted every time the ngInclude content is requested. 37929 * 37930 * @param {Object} angularEvent Synthetic event object. 37931 * @param {String} src URL of content to load. 37932 */ 37933 37934 37935/** 37936 * @ngdoc event 37937 * @name ngInclude#$includeContentLoaded 37938 * @eventType emit on the current ngInclude scope 37939 * @description 37940 * Emitted every time the ngInclude content is reloaded. 37941 * 37942 * @param {Object} angularEvent Synthetic event object. 37943 * @param {String} src URL of content to load. 37944 */ 37945 37946 37947/** 37948 * @ngdoc event 37949 * @name ngInclude#$includeContentError 37950 * @eventType emit on the scope ngInclude was declared in 37951 * @description 37952 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299) 37953 * 37954 * @param {Object} angularEvent Synthetic event object. 37955 * @param {String} src URL of content to load. 37956 */ 37957var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', 37958 function($templateRequest, $anchorScroll, $animate) { 37959 return { 37960 restrict: 'ECA', 37961 priority: 400, 37962 terminal: true, 37963 transclude: 'element', 37964 controller: angular.noop, 37965 compile: function(element, attr) { 37966 var srcExp = attr.ngInclude || attr.src, 37967 onloadExp = attr.onload || '', 37968 autoScrollExp = attr.autoscroll; 37969 37970 return function(scope, $element, $attr, ctrl, $transclude) { 37971 var changeCounter = 0, 37972 currentScope, 37973 previousElement, 37974 currentElement; 37975 37976 var cleanupLastIncludeContent = function() { 37977 if (previousElement) { 37978 previousElement.remove(); 37979 previousElement = null; 37980 } 37981 if (currentScope) { 37982 currentScope.$destroy(); 37983 currentScope = null; 37984 } 37985 if (currentElement) { 37986 $animate.leave(currentElement).done(function(response) { 37987 if (response !== false) previousElement = null; 37988 }); 37989 previousElement = currentElement; 37990 currentElement = null; 37991 } 37992 }; 37993 37994 scope.$watch(srcExp, function ngIncludeWatchAction(src) { 37995 var afterAnimation = function(response) { 37996 if (response !== false && isDefined(autoScrollExp) && 37997 (!autoScrollExp || scope.$eval(autoScrollExp))) { 37998 $anchorScroll(); 37999 } 38000 }; 38001 var thisChangeId = ++changeCounter; 38002 38003 if (src) { 38004 //set the 2nd param to true to ignore the template request error so that the inner 38005 //contents and scope can be cleaned up. 38006 $templateRequest(src, true).then(function(response) { 38007 if (scope.$$destroyed) return; 38008 38009 if (thisChangeId !== changeCounter) return; 38010 var newScope = scope.$new(); 38011 ctrl.template = response; 38012 38013 // Note: This will also link all children of ng-include that were contained in the original 38014 // html. If that content contains controllers, ... they could pollute/change the scope. 38015 // However, using ng-include on an element with additional content does not make sense... 38016 // Note: We can't remove them in the cloneAttchFn of $transclude as that 38017 // function is called before linking the content, which would apply child 38018 // directives to non existing elements. 38019 var clone = $transclude(newScope, function(clone) { 38020 cleanupLastIncludeContent(); 38021 $animate.enter(clone, null, $element).done(afterAnimation); 38022 }); 38023 38024 currentScope = newScope; 38025 currentElement = clone; 38026 38027 currentScope.$emit('$includeContentLoaded', src); 38028 scope.$eval(onloadExp); 38029 }, function() { 38030 if (scope.$$destroyed) return; 38031 38032 if (thisChangeId === changeCounter) { 38033 cleanupLastIncludeContent(); 38034 scope.$emit('$includeContentError', src); 38035 } 38036 }); 38037 scope.$emit('$includeContentRequested', src); 38038 } else { 38039 cleanupLastIncludeContent(); 38040 ctrl.template = null; 38041 } 38042 }); 38043 }; 38044 } 38045 }; 38046}]; 38047 38048// This directive is called during the $transclude call of the first `ngInclude` directive. 38049// It will replace and compile the content of the element with the loaded template. 38050// We need this directive so that the element content is already filled when 38051// the link function of another directive on the same element as ngInclude 38052// is called. 38053var ngIncludeFillContentDirective = ['$compile', 38054 function($compile) { 38055 return { 38056 restrict: 'ECA', 38057 priority: -400, 38058 require: 'ngInclude', 38059 link: function(scope, $element, $attr, ctrl) { 38060 if (toString.call($element[0]).match(/SVG/)) { 38061 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not 38062 // support innerHTML, so detect this here and try to generate the contents 38063 // specially. 38064 $element.empty(); 38065 $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope, 38066 function namespaceAdaptedClone(clone) { 38067 $element.append(clone); 38068 }, {futureParentElement: $element}); 38069 return; 38070 } 38071 38072 $element.html(ctrl.template); 38073 $compile($element.contents())(scope); 38074 } 38075 }; 38076 }]; 38077 38078/** 38079 * @ngdoc directive 38080 * @name ngInit 38081 * @restrict AC 38082 *
38082 38083 * @description 38084 * The `ngInit` directive allows you to evaluate an expression in the 38085 * current scope. 38086 * 38087 * <div class="alert alert-danger"> 38088 * This directive can be abused to add unnecessary amounts of logic into your templates. 38089 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of 38090 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via 38091 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers} 38092 * rather than `ngInit` to initialize values on a scope. 38093 * </div> 38094 * 38095 * <div class="alert alert-warning"> 38096 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make 38097 * sure you have parentheses to ensure correct operator precedence: 38098 * <pre class="prettyprint"> 38099 * `<div ng-init="test1 = ($index | toString)"></div>` 38100 * </pre> 38101 * </div> 38102 * 38103 * @priority 450 38104 * 38105 * @element ANY 38106 * @param {expression} ngInit {@link guide/expression Expression} to eval. 38107 * 38108 * @example 38109 <example module="initExample" name="ng-init"> 38110 <file name="index.html"> 38111 <script> 38112 angular.module('initExample', []) 38113 .controller('ExampleController', ['$scope', function($scope) { 38114 $scope.list = [['a', 'b'], ['c', 'd']]; 38115 }]); 38116 </script>
38117 <div ng-controller="ExampleController"> 38118 <div ng-repeat="innerList in list" ng-init="outerIndex = $index"> 38119 <div ng-repeat="value in innerList" ng-init="innerIndex = $index"> 38120 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span> 38121 </div> 38122 </div> 38123 </div> 38124 </file> 38125 <file name="protractor.js" type="protractor"> 38126 it('should alias index positions', function() { 38127 var elements = element.all(by.css('.example-init')); 38128 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;'); 38129 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;'); 38130 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;'); 38131 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;'); 38132 }); 38133 </file> 38134 </example> 38135 */ 38136var ngInitDirective = ngDirective({ 38137 priority: 450, 38138 compile: function() { 38139 return { 38140 pre: function(scope, element, attrs) { 38141 scope.$eval(attrs.ngInit); 38142 } 38143 }; 38144 } 38145}); 38146 38147/** 38148 * @ngdoc directive 38149 * @name ngList 38150 * 38151 * @description 38152 * Text input that converts between a delimited string and an array of strings. The default 38153 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom 38154 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`. 38155 * 38156 * The behaviour of the directive is affected by the use of the `ngTrim` attribute. 38157 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each 38158 * list item is respected. This implies that the user of the directive is responsible for 38159 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a 38160 * tab or newline character. 38161 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected 38162 * when joining the list items back together) and whitespace around each list item is stripped 38163 * before it is added to the model. 38164 * 38165 * ### Example with Validation 38166 * 38167 * <example name="ngList-directive" module="listExample"> 38168 * <file name="app.js"> 38169 * angular.module('listExample', []) 38170 * .controller('ExampleController', ['$scope', function($scope) { 38171 * $scope.names = ['morpheus', 'neo', 'trinity']; 38172 * }]); 38173 * </file> 38174 * <file name="index.html">
38175 * <form name="myForm" ng-controller="ExampleController"> 38176 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label> 38177 * <span role="alert"> 38178 * <span class="error" ng-show="myForm.namesInput.$error.required"> 38179 * Required!</span> 38180 * </span> 38181 * <br> 38182 * <tt>names = {{names}}</tt><br/> 38183 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/> 38184 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/> 38185 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 38186 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 38187 * </form> 38188 * </file> 38189 * <file name="protractor.js" type="protractor"> 38190 * var listInput = element(by.model('names')); 38191 * var names = element(by.exactBinding('names')); 38192 * var valid = element(by.binding('myForm.namesInput.$valid')); 38193 * var error = element(by.css('span.error')); 38194 * 38195 * it('should initialize to model', function() { 38196 * expect(names.getText()).toContain('["morpheus","neo","trinity"]'); 38197 * expect(valid.getText()).toContain('true'); 38198 * expect(error.getCssValue('display')).toBe('none'); 38199 * }); 38200 * 38201 * it('should be invalid if empty', function() { 38202 * listInput.clear(); 38203 * listInput.sendKeys(''); 38204 * 38205 * expect(names.getText()).toContain(''); 38206 * expect(valid.getText()).toContain('false'); 38207 * expect(error.getCssValue('display')).not.toBe('none'); 38208 * }); 38209 * </file> 38210 * </example> 38211 * 38212 * ### Example - splitting on newline 38213 * <example name="ngList-directive-newlines"> 38214 * <file name="index.html"> 38215 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea> 38216 * <pre>{{ list | json }}</pre> 38217 * </file> 38218 * <file name="protractor.js" type="protractor"> 38219 * it("should split the text by newlines", function() { 38220 * var listInput = element(by.model('list')); 38221 * var output = element(by.binding('list | json')); 38222 * listInput.sendKeys('abc\ndef\nghi'); 38223 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]'); 38224 * }); 38225 * </file> 38226 * </example> 38227 * 38228 * @element input 38229 * @param {string=} ngList optional delimiter that should be used to split the value. 38230 */ 38231var ngListDirective = function() { 38232 return { 38233 restrict: 'A', 38234 priority: 100, 38235 require: 'ngModel', 38236 link: function(scope, element, attr, ctrl) { 38237 var ngList = attr.ngList || ', '; 38238 var trimValues = attr.ngTrim !== 'false'; 38239 var separator = trimValues ? trim(ngList) : ngList; 38240 38241 var parse = function(viewValue) { 38242 // If the viewValue is invalid (say required but empty) it will be `undefined` 38243 if (isUndefined(viewValue)) return; 38244 38245 var list = []; 38246 38247 if (viewValue) { 38248 forEach(viewValue.split(separator), function(value) { 38249 if (value) list.push(trimValues ? trim(value) : value); 38250 }); 38251 } 38252 38253 return list; 38254 }; 38255 38256 ctrl.$parsers.push(parse); 38257 ctrl.$formatters.push(function(value) { 38258 if (isArray(value)) { 38259 return value.join(ngList); 38260 } 38261 38262 return undefined; 38263 }); 38264 38265 // Override the standard $isEmpty because an empty array means the input is empty. 38266 ctrl.$isEmpty = function(value) { 38267 return !value || !value.length; 38268 }; 38269 } 38270 }; 38271}; 38272 38273/* global VALID_CLASS: true, 38274 INVALID_CLASS: true, 38275 PRISTINE_CLASS: true, 38276 DIRTY_CLASS: true, 38277 UNTOUCHED_CLASS: true, 38278 TOUCHED_CLASS: true, 38279 PENDING_CLASS: true, 38280 addSetValidityMethod: true, 38281 setupValidity: true, 38282 defaultModelOptions: false 38283*/ 38284 38285 38286var VALID_CLASS = 'ng-valid', 38287 INVALID_CLASS = 'ng-invalid', 38288 PRISTINE_CLASS = 'ng-pristine', 38289 DIRTY_CLASS = 'ng-dirty', 38290 UNTOUCHED_CLASS = 'ng-untouched', 38291 TOUCHED_CLASS = 'ng-touched', 38292 EMPTY_CLASS = 'ng-empty', 38293 NOT_EMPTY_CLASS = 'ng-not-empty'; 38294 38295var ngModelMinErr = minErr('ngModel'); 38296 38297/** 38298 * @ngdoc type 38299 * @name ngModel.NgModelController 38300 * 38301 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a 38302 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue 38303 * is set. 38304 * 38305 * @property {*} $modelValue The value in the model that the control is bound to. 38306 * 38307 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever 38308 * the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue 38309 `$viewValue`} from the DOM, usually via user input. 38310 See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation. 38311 Note that the `$parsers` are not called when the bound ngModel expression changes programmatically. 38312 38313 The functions are called in array order, each passing 38314 its return value through to the next. The last return value is forwarded to the 38315 {@link ngModel.NgModelController#$validators `$validators`} collection. 38316 38317 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue 38318 `$viewValue`}. 38319 38320 Returning `undefined` from a parser means a parse error occurred. In that case, 38321 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel` 38322 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`} 38323 is set to `true`. The parse error is stored in `ngModel.$error.parse`. 38324 38325 This simple example shows a parser that would convert text input value to lowercase: 38326 * ```js 38327 * function parse(value) { 38328 * if (value) { 38329 * return value.toLowerCase(); 38330 * } 38331 * } 38332 * ngModelController.$parsers.push(parse); 38333 * ``` 38334 38335 * 38336 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever 38337 the bound ngModel expression changes programmatically. The `$formatters` are not called when the 38338 value of the control is changed by user interaction. 38339 38340 Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue 38341 `$modelValue`} for display in the control. 38342 38343 The functions are called in reverse array order, each passing the value through to the 38344 next. The last return value is used as the actual DOM value. 38345 38346 This simple example shows a formatter that would convert the model value to uppercase: 38347 38348 * ```js 38349 * function format(value) { 38350 * if (value) { 38351 * return value.toUpperCase(); 38352 * } 38353 * } 38354 * ngModel.$formatters.push(format); 38355 * ``` 38356 * 38357 * @property {Object.<string, function>} $validators A collection of validators that are applied 38358 * whenever the model value changes. The key value within the object refers to the name of the 38359 * validator while the function refers to the validation operation. The validation operation is 38360 * provided with the model value as an argument and must return a true or false value depending 38361 * on the response of that validation. 38362 * 38363 * ```js 38364 * ngModel.$validators.validCharacters = function(modelValue, viewValue) { 38365 * var value = modelValue || viewValue; 38366 * return /[0-9]+/.test(value) && 38367 * /[a-z]+/.test(value) && 38368 * /[A-Z]+/.test(value) && 38369 * /\W+/.test(value); 38370 * }; 38371 * ``` 38372 * 38373 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to 38374 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided 38375 * is expected to return a promise when it is run during the model validation process. Once the promise 38376 * is delivered then the validation status will be set to true when fulfilled and false when rejected. 38377 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model 38378 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator 38379 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators 38380 * will only run once all synchronous validators have passed. 38381 * 38382 * Please note that if $http is used then it is important that the server returns a success HTTP response code 38383 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation. 38384 * 38385 * ```js 38386 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) { 38387 * var value = modelValue || viewValue; 38388 * 38389 * // Lookup user by username 38390 * return $http.get('/api/users/' + value). 38391 * then(function resolved() { 38392 * //username exists, this means validation fails 38393 * return $q.reject('exists'); 38394 * }, function rejected() { 38395 * //username does not exist, therefore this validation passes 38396 * return true; 38397 * }); 38398 * }; 38399 * ``` 38400 * 38401 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the 38402 * view value has changed. It is called with no arguments, and its return value is ignored. 38403 * This can be used in place of additional $watches against the model value. 38404 * 38405 * @property {Object} $error An object hash with all failing validator ids as keys. 38406 * @property {Object} $pending An object hash with all pending validator ids as keys. 38407 * 38408 * @property {boolean} $untouched True if control has not lost focus yet. 38409 * @property {boolean} $touched True if control has lost focus. 38410 * @property {boolean} $pristine True if user has not interacted with the control yet. 38411 * @property {boolean} $dirty True if user has already interacted with the control. 38412 * @property {boolean} $valid True if there is no error. 38413 * @property {boolean}
38413 $invalid True if at least one error on the control. 38414 * @property {string} $name The name attribute of the control. 38415 * 38416 * @description 38417 * 38418 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive. 38419 * The controller contains services for data-binding, validation, CSS updates, and value formatting 38420 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or 38421 * listening to DOM events. 38422 * Such DOM related logic should be provided by other directives which make use of 38423 * `NgModelController` for data-binding to control elements. 38424 * Angular provides this DOM logic for most {@link input `input`} elements. 38425 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example 38426 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements. 38427 * 38428 * @example 38429 * ### Custom Control Example 38430 * This example shows how to use `NgModelController` with a custom control to achieve 38431 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) 38432 * collaborate together to achieve the desired result. 38433 * 38434 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element 38435 * contents be edited in place by the user. 38436 * 38437 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} 38438 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`). 38439 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks 38440 * that content using the `$sce` service. 38441 * 38442 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js"> 38443 <file name="style.css"> 38444 [contenteditable] { 38445 border: 1px solid black; 38446 background-color: white; 38447 min-height: 20px; 38448 } 38449 38450 .ng-invalid { 38451 border: 1px solid red; 38452 } 38453 38454 </file> 38455 <file name="script.js"> 38456 angular.module('customControl', ['ngSanitize']). 38457 directive('contenteditable', ['$sce', function($sce) { 38458 return { 38459 restrict: 'A', // only activate on element attribute 38460 require: '?ngModel', // get a hold of NgModelController 38461 link: function(scope, element, attrs, ngModel) { 38462 if (!ngModel) return; // do nothing if no ng-model 38463 38464 // Specify how UI should be updated 38465 ngModel.$render = function() { 38466 element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); 38467 }; 38468 38469 // Listen for change events to enable binding 38470 element.on('blur keyup change', function() { 38471 scope.$evalAsync(read); 38472 }); 38473 read(); // initialize 38474 38475 // Write data to the model 38476 function read() { 38477 var html = element.html(); 38478 // When we clear the content editable the browser leaves a <br> behind 38479 // If strip-br attribute is provided then we strip this out 38480 if (attrs.stripBr && html === '<br>') { 38481 html = ''; 38482 } 38483 ngModel.$setViewValue(html); 38484 } 38485 } 38486 }; 38487 }]); 38488 </file> 38489 <file name="index.html"> 38490 <form name="myForm"> 38491 <div contenteditable 38492 name="myWidget" ng-model="userContent" 38493 strip-br="true" 38494 required>Change me!</div> 38495 <span ng-show="myForm.myWidget.$error.required">Required!</span> 38496 <hr> 38497 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea> 38498 </form> 38499 </file> 38500 <file name="protractor.js" type="protractor"> 38501 it('should data-bind and become invalid', function() { 38502 if (browser.params.browser === 'safari' || browser.params.browser === 'firefox') { 38503 // SafariDriver can't handle contenteditable 38504 // and Firefox driver can't clear contenteditables very well 38505 return; 38506 } 38507 var contentEditable = element(by.css('[contenteditable]')); 38508 var content = 'Change me!'; 38509 38510 expect(contentEditable.getText()).toEqual(content); 38511 38512 contentEditable.clear(); 38513 contentEditable.sendKeys(protractor.Key.BACK_SPACE); 38514 expect(contentEditable.getText()).toEqual(''); 38515 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/); 38516 }); 38517 </file> 38518 * </example> 38519 * 38520 * 38521 */ 38522NgModelController.$inject = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$q', '$interpolate']; 38523function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $q, $interpolate) { 38524 this.$viewValue = Number.NaN; 38525 this.$modelValue = Number.NaN; 38526 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity. 38527 this.$validators = {}; 38528 this.$asyncValidators = {}; 38529 this.$parsers = []; 38530 this.$formatters = []; 38531 this.$viewChangeListeners = []; 38532 this.$untouched = true; 38533 this.$touched = false;
vendor: 4,653 bytes, lines 38534-38661
38534 this.$pristine = true; 38535 this.$dirty = false; 38536 this.$valid = true; 38537 this.$invalid = false; 38538 this.$error = {}; // keep invalid keys here 38539 this.$$success = {}; // keep valid keys here 38540 this.$pending = undefined; // keep pending keys here 38541 this.$name = $interpolate($attr.name || '', false)($scope); 38542 this.$$parentForm = nullFormCtrl; 38543 this.$options = defaultModelOptions; 38544 38545 this.$$parsedNgModel = $parse($attr.ngModel); 38546 this.$$parsedNgModelAssign = this.$$parsedNgModel.assign; 38547 this.$$ngModelGet = this.$$parsedNgModel; 38548 this.$$ngModelSet = this.$$parsedNgModelAssign; 38549 this.$$pendingDebounce = null; 38550 this.$$parserValid = undefined; 38551 38552 this.$$currentValidationRunId = 0; 38553 38554 this.$$scope = $scope; 38555 this.$$attr = $attr; 38556 this.$$element = $element; 38557 this.$$animate = $animate; 38558 this.$$timeout = $timeout; 38559 this.$$parse = $parse; 38560 this.$$q = $q; 38561 this.$$exceptionHandler = $exceptionHandler; 38562 38563 setupValidity(this); 38564 setupModelWatcher(this); 38565} 38566 38567NgModelController.prototype = { 38568 $$initGetterSetters: function() { 38569 if (this.$options.getOption('getterSetter')) { 38570 var invokeModelGetter = this.$$parse(this.$$attr.ngModel + '()'), 38571 invokeModelSetter = this.$$parse(this.$$attr.ngModel + '($$$p)'); 38572 38573 this.$$ngModelGet = function($scope) { 38574 var modelValue = this.$$parsedNgModel($scope); 38575 if (isFunction(modelValue)) { 38576 modelValue = invokeModelGetter($scope); 38577 } 38578 return modelValue; 38579 }; 38580 this.$$ngModelSet = function($scope, newValue) { 38581 if (isFunction(this.$$parsedNgModel($scope))) { 38582 invokeModelSetter($scope, {$$$p: newValue}); 38583 } else { 38584 this.$$parsedNgModelAssign($scope, newValue); 38585 } 38586 }; 38587 } else if (!this.$$parsedNgModel.assign) { 38588 throw ngModelMinErr('nonassign', 'Expression \'{0}\' is non-assignable. Element: {1}', 38589 this.$$attr.ngModel, startingTag(this.$$element)); 38590 } 38591 }, 38592 38593 38594 /** 38595 * @ngdoc method 38596 * @name ngModel.NgModelController#$render 38597 * 38598 * @description 38599 * Called when the view needs to be updated. It is expected that the user of the ng-model 38600 * directive will implement this method. 38601 * 38602 * The `$render()` method is invoked in the following situations: 38603 * 38604 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last 38605 * committed value then `$render()` is called to update the input control. 38606 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and 38607 * the `$viewValue` are different from last time. 38608 * 38609 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of 38610 * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue` 38611 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be 38612 * invoked if you only change a property on the objects. 38613 */ 38614 $render: noop, 38615 38616 /** 38617 * @ngdoc method 38618 * @name ngModel.NgModelController#$isEmpty 38619 * 38620 * @description 38621 * This is called when we need to determine if the value of an input is empty. 38622 * 38623 * For instance, the required directive does this to work out if the input has data or not. 38624 * 38625 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. 38626 * 38627 * You can override this for input directives whose concept of being empty is different from the 38628 * default. The `checkboxInputType` directive does this because in its case a value of `false` 38629 * implies empty. 38630 * 38631 * @param {*} value The value of the input to check for emptiness. 38632 * @returns {boolean} True if `value` is "empty". 38633 */ 38634 $isEmpty: function(value) { 38635 // eslint-disable-next-line no-self-compare 38636 return isUndefined(value) || value === '' || value === null || value !== value; 38637 }, 38638 38639 $$updateEmptyClasses: function(value) { 38640 if (this.$isEmpty(value)) { 38641 this.$$animate.removeClass(this.$$element, NOT_EMPTY_CLASS); 38642 this.$$animate.addClass(this.$$element, EMPTY_CLASS); 38643 } else { 38644 this.$$animate.removeClass(this.$$element, EMPTY_CLASS); 38645 this.$$animate.addClass(this.$$element, NOT_EMPTY_CLASS); 38646 } 38647 }, 38648 38649 /** 38650 * @ngdoc method 38651 * @name ngModel.NgModelController#$setPristine 38652 * 38653 * @description 38654 * Sets the control to its pristine state. 38655 * 38656 * This method can be called to remove the `ng-dirty` class and set the control to its pristine 38657 * state (`ng-pristine` class). A model is considered to be pristine when the control 38658 * has not been changed from when first compiled. 38659 */ 38660 $setPristine: function() { 38661 this.$dirty = false;
38662 this.$pristine = true; 38663 this.$$animate.removeClass(this.$$element, DIRTY_CLASS); 38664 this.$$animate.addClass(this.$$element, PRISTINE_CLASS); 38665 }, 38666 38667 /** 38668 * @ngdoc method 38669 * @name ngModel.NgModelController#$setDirty 38670 * 38671 * @description 38672 * Sets the control to its dirty state. 38673 * 38674 * This method can be called to remove the `ng-pristine` class and set the control to its dirty 38675 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed 38676 * from when first compiled. 38677 */ 38678 $setDirty: function() { 38679 this.$dirty = true; 38680 this.$pristine = false; 38681 this.$$animate.removeClass(this.$$element, PRISTINE_CLASS); 38682 this.$$animate.addClass(this.$$element, DIRTY_CLASS); 38683 this.$$parentForm.$setDirty(); 38684 }, 38685 38686 /** 38687 * @ngdoc method 38688 * @name ngModel.NgModelController#$setUntouched 38689 * 38690 * @description 38691 * Sets the control to its untouched state. 38692 * 38693 * This method can be called to remove the `ng-touched` class and set the control to its 38694 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched 38695 * by default, however this function can be used to restore that state if the model has 38696 * already been touched by the user. 38697 */ 38698 $setUntouched: function() { 38699 this.$touched = false; 38700 this.$untouched = true; 38701 this.$$animate.setClass(this.$$element, UNTOUCHED_CLASS, TOUCHED_CLASS); 38702 }, 38703 38704 /** 38705 * @ngdoc method 38706 * @name ngModel.NgModelController#$setTouched 38707 * 38708 * @description 38709 * Sets the control to its touched state. 38710 * 38711 * This method can be called to remove the `ng-untouched` class and set the control to its 38712 * touched state (`ng-touched` class). A model is considered to be touched when the user has 38713 * first focused the control element and then shifted focus away from the control (blur event). 38714 */ 38715 $setTouched: function() { 38716 this.$touched = true; 38717 this.$untouched = false; 38718 this.$$animate.setClass(this.$$element, TOUCHED_CLASS, UNTOUCHED_CLASS); 38719 }, 38720 38721 /** 38722 * @ngdoc method 38723 * @name ngModel.NgModelController#$rollbackViewValue 38724 * 38725 * @description 38726 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`, 38727 * which may be caused by a pending debounced event or because the input is waiting for some 38728 * future event. 38729 * 38730 * If you have an input that uses `ng-model-options` to set up debounced updates or updates that 38731 * depend on special events such as `blur`, there can be a period when the `$viewValue` is out of 38732 * sync with the ngModel's `$modelValue`. 38733 * 38734 * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update 38735 * and reset the input to the last committed view value. 38736 * 38737 * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue` 38738 * programmatically before these debounced/future events have resolved/occurred, because Angular's 38739 * dirty checking mechanism is not able to tell whether the model has actually changed or not. 38740 * 38741 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an 38742 * input which may have such events pending. This is important in order to make sure that the 38743 * input field will be updated with the new model value and any pending operations are cancelled. 38744 * 38745 * <example name="ng-model-cancel-update" module="cancel-update-example"> 38746 * <file name="app.js"> 38747 * angular.module('cancel-update-example', []) 38748 * 38749 * .controller('CancelUpdateController', ['$scope', function($scope) { 38750 * $scope.model = {value1: '', value2: ''}; 38751 * 38752 * $scope.setEmpty = function(e, value, rollback) { 38753 * if (e.keyCode === 27) { 38754 * e.preventDefault(); 38755 * if (rollback) { 38756 * $scope.myForm[value].$rollbackViewValue(); 38757 * } 38758 * $scope.model[value] = ''; 38759 * } 38760 * }; 38761 * }]); 38762 * </file> 38763 * <file name="index.html"> 38764 * <div ng-controller="CancelUpdateController"> 38765 * <p>
38765Both of these inputs are only updated if they are blurred. Hitting escape should 38766 * empty them. Follow these steps and observe the difference:</p> 38767 * <ol> 38768 * <li>Type something in the input. You will see that the model is not yet updated</li> 38769 * <li>Press the Escape key. 38770 * <ol> 38771 * <li> In the first example, nothing happens, because the model is already '', and no 38772 * update is detected. If you blur the input, the model will be set to the current view. 38773 * </li> 38774 * <li> In the second example, the pending update is cancelled, and the input is set back 38775 * to the last committed view value (''). Blurring the input does nothing. 38776 * </li> 38777 * </ol> 38778 * </li> 38779 * </ol> 38780 * 38781 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }"> 38782 * <div> 38783 * <p id="inputDescription1">Without $rollbackViewValue():</p> 38784 * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1" 38785 * ng-keydown="setEmpty($event, 'value1')"> 38786 * value1: "{{ model.value1 }}" 38787 * </div> 38788 * 38789 * <div> 38790 * <p id="inputDescription2">With $rollbackViewValue():</p> 38791 * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2" 38792 * ng-keydown="setEmpty($event, 'value2', true)"> 38793 * value2: "{{ model.value2 }}" 38794 * </div> 38795 * </form> 38796 * </div> 38797 * </file> 38798 <file name="style.css"> 38799 div { 38800 display: table-cell; 38801 } 38802 div:nth-child(1) { 38803 padding-right: 30px; 38804 } 38805 38806 </file> 38807 * </example> 38808 */ 38809 $rollbackViewValue: function() { 38810 this.$$timeout.cancel(this.$$pendingDebounce); 38811 this.$viewValue = this.$$lastCommittedViewValue; 38812 this.$render(); 38813 }, 38814 38815 /** 38816 * @ngdoc method 38817 * @name ngModel.NgModelController#$validate 38818 * 38819 * @description 38820 * Runs each of the registered validators (first synchronous validators and then 38821 * asynchronous validators). 38822 * If the validity changes to invalid, the model will be set to `undefined`, 38823 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`. 38824 * If the validity changes to valid, it will set the model to the last available valid 38825 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope. 38826 */ 38827 $validate: function() { 38828 // ignore $validate before model is initialized 38829 if (isNumberNaN(this.$modelValue)) { 38830 return; 38831 } 38832 38833 var viewValue = this.$$lastCommittedViewValue; 38834 // Note: we use the $$rawModelValue as $modelValue might have been 38835 // set to undefined during a view -> model update that found validation 38836 // errors. We can't parse the view here, since that could change 38837 // the model although neither viewValue nor the model on the scope changed 38838 var modelValue = this.$$rawModelValue; 38839 38840 var prevValid = this.$valid; 38841 var prevModelValue = this.$modelValue; 38842 38843 var allowInvalid = this.$options.getOption('allowInvalid'); 38844 38845 var that = this; 38846 this.$$runValidators(modelValue, viewValue, function(allValid) { 38847 // If there was no change in validity, don't update the model 38848 // This prevents changing an invalid modelValue to undefined 38849 if (!allowInvalid && prevValid !== allValid) { 38850 // Note: Don't check this.$valid here, as we could have 38851 // external validators (e.g. calculated on the server), 38852 // that just call $setValidity and need the model value 38853 // to calculate their validity. 38854 that.$modelValue = allValid ? modelValue : undefined; 38855 38856 if (that.$modelValue !== prevModelValue) { 38857 that.$$writeModelToScope(); 38858 } 38859 } 38860 }); 38861 }, 38862 38863 $$runValidators: function(modelValue, viewValue, doneCallback) { 38864 this.$$currentValidationRunId++; 38865 var localValidationRunId = this.$$currentValidationRunId; 38866 var that = this; 38867 38868 // check parser error 38869 if (!processParseErrors()) { 38870 validationDone(false); 38871 return; 38872 } 38873 if (!processSyncValidators()) { 38874 validationDone(false); 38875 return; 38876 } 38877 processAsyncValidators(); 38878 38879 function processParseErrors() { 38880 var errorKey = that.$$parserName || 'parse'; 38881 if (isUndefined(that.$$parserValid)) {
vendor: 9,425 bytes, lines 38882-39121
38882 setValidity(errorKey, null); 38883 } else { 38884 if (!that.$$parserValid) { 38885 forEach(that.$validators, function(v, name) { 38886 setValidity(name, null); 38887 }); 38888 forEach(that.$asyncValidators, function(v, name) { 38889 setValidity(name, null); 38890 }); 38891 } 38892 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName 38893 setValidity(errorKey, that.$$parserValid); 38894 return that.$$parserValid; 38895 } 38896 return true; 38897 } 38898 38899 function processSyncValidators() { 38900 var syncValidatorsValid = true; 38901 forEach(that.$validators, function(validator, name) { 38902 var result = Boolean(validator(modelValue, viewValue)); 38903 syncValidatorsValid = syncValidatorsValid && result; 38904 setValidity(name, result); 38905 }); 38906 if (!syncValidatorsValid) { 38907 forEach(that.$asyncValidators, function(v, name) { 38908 setValidity(name, null); 38909 }); 38910 return false; 38911 } 38912 return true; 38913 } 38914 38915 function processAsyncValidators() { 38916 var validatorPromises = []; 38917 var allValid = true; 38918 forEach(that.$asyncValidators, function(validator, name) { 38919 var promise = validator(modelValue, viewValue); 38920 if (!isPromiseLike(promise)) { 38921 throw ngModelMinErr('nopromise', 38922 'Expected asynchronous validator to return a promise but got \'{0}\' instead.', promise); 38923 } 38924 setValidity(name, undefined); 38925 validatorPromises.push(promise.then(function() { 38926 setValidity(name, true); 38927 }, function() { 38928 allValid = false; 38929 setValidity(name, false); 38930 })); 38931 }); 38932 if (!validatorPromises.length) { 38933 validationDone(true); 38934 } else { 38935 that.$$q.all(validatorPromises).then(function() { 38936 validationDone(allValid); 38937 }, noop); 38938 } 38939 } 38940 38941 function setValidity(name, isValid) { 38942 if (localValidationRunId === that.$$currentValidationRunId) { 38943 that.$setValidity(name, isValid); 38944 } 38945 } 38946 38947 function validationDone(allValid) { 38948 if (localValidationRunId === that.$$currentValidationRunId) { 38949 38950 doneCallback(allValid); 38951 } 38952 } 38953 }, 38954 38955 /** 38956 * @ngdoc method 38957 * @name ngModel.NgModelController#$commitViewValue 38958 * 38959 * @description 38960 * Commit a pending update to the `$modelValue`. 38961 * 38962 * Updates may be pending by a debounced event or because the input is waiting for a some future 38963 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController` 38964 * usually handles calling this in response to input events. 38965 */ 38966 $commitViewValue: function() { 38967 var viewValue = this.$viewValue; 38968 38969 this.$$timeout.cancel(this.$$pendingDebounce); 38970 38971 // If the view value has not changed then we should just exit, except in the case where there is 38972 // a native validator on the element. In this case the validation state may have changed even though 38973 // the viewValue has stayed empty. 38974 if (this.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !this.$$hasNativeValidators)) { 38975 return; 38976 } 38977 this.$$updateEmptyClasses(viewValue); 38978 this.$$lastCommittedViewValue = viewValue; 38979 38980 // change to dirty 38981 if (this.$pristine) { 38982 this.$setDirty(); 38983 } 38984 this.$$parseAndValidate(); 38985 }, 38986 38987 $$parseAndValidate: function() { 38988 var viewValue = this.$$lastCommittedViewValue; 38989 var modelValue = viewValue; 38990 var that = this; 38991 38992 this.$$parserValid = isUndefined(modelValue) ? undefined : true; 38993 38994 if (this.$$parserValid) { 38995 for (var i = 0; i < this.$parsers.length; i++) { 38996 modelValue = this.$parsers[i](modelValue); 38997 if (isUndefined(modelValue)) { 38998 this.$$parserValid = false; 38999 break; 39000 } 39001 } 39002 } 39003 if (isNumberNaN(this.$modelValue)) { 39004 // this.$modelValue has not been touched yet... 39005 this.$modelValue = this.$$ngModelGet(this.$$scope); 39006 } 39007 var prevModelValue = this.$modelValue; 39008 var allowInvalid = this.$options.getOption('allowInvalid'); 39009 this.$$rawModelValue = modelValue; 39010 39011 if (allowInvalid) { 39012 this.$modelValue = modelValue; 39013 writeToModelIfNeeded(); 39014 } 39015 39016 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date. 39017 // This can happen if e.g. $setViewValue is called from inside a parser 39018 this.$$runValidators(modelValue, this.$$lastCommittedViewValue, function(allValid) { 39019 if (!allowInvalid) { 39020 // Note: Don't check this.$valid here, as we could have 39021 // external validators (e.g. calculated on the server), 39022 // that just call $setValidity and need the model value 39023 // to calculate their validity. 39024 that.$modelValue = allValid ? modelValue : undefined; 39025 writeToModelIfNeeded(); 39026 } 39027 }); 39028 39029 function writeToModelIfNeeded() { 39030 if (that.$modelValue !== prevModelValue) { 39031 that.$$writeModelToScope(); 39032 } 39033 } 39034 }, 39035 39036 $$writeModelToScope: function() { 39037 this.$$ngModelSet(this.$$scope, this.$modelValue); 39038 forEach(this.$viewChangeListeners, function(listener) { 39039 try { 39040 listener(); 39041 } catch (e) { 39042 // eslint-disable-next-line no-invalid-this 39043 this.$$exceptionHandler(e); 39044 } 39045 }, this); 39046 }, 39047 39048 /** 39049 * @ngdoc method 39050 * @name ngModel.NgModelController#$setViewValue 39051 * 39052 * @description 39053 * Update the view value. 39054 * 39055 * This method should be called when a control wants to change the view value; typically, 39056 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input} 39057 * directive calls it when the value of the input changes and {@link ng.directive:select select} 39058 * calls it when an option is selected. 39059 * 39060 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers` 39061 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged 39062 * value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and 39063 * `$asyncValidators` are called and the value is applied to `$modelValue`. 39064 * Finally, the value is set to the **expression** specified in the `ng-model` attribute and 39065 * all the registered change listeners, in the `$viewChangeListeners` list are called. 39066 * 39067 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn` 39068 * and the `default` trigger is not listed, all those actions will remain pending until one of the 39069 * `updateOn` events is triggered on the DOM element. 39070 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions} 39071 * directive is used with a custom debounce for this particular event. 39072 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce` 39073 * is specified, once the timer runs out. 39074 * 39075 * When used with standard inputs, the view value will always be a string (which is in some cases 39076 * parsed into another type, such as a `Date` object for `input[date]`.) 39077 * However, custom controls might also pass objects to this method. In this case, we should make 39078 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not 39079 * perform a deep watch of objects, it only looks for a change of identity. If you only change 39080 * the property of the object then ngModel will not realize that the object has changed and 39081 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should 39082 * not change properties of the copy once it has been passed to `$setViewValue`. 39083 * Otherwise you may cause the model value on the scope to change incorrectly. 39084 * 39085 * <div class="alert alert-info"> 39086 * In any case, the value passed to the method should always reflect the current value 39087 * of the control. For example, if you are calling `$setViewValue` for an input element, 39088 * you should pass the input DOM value. Otherwise, the control and the scope model become 39089 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change 39090 * the control's DOM value in any way. If we want to change the control's DOM value 39091 * programmatically, we should update the `ngModel` scope expression. Its new value will be 39092 * picked up by the model controller, which will run it through the `$formatters`, `$render` it 39093 * to update the DOM, and finally call `$validate` on it. 39094 * </div> 39095 * 39096 * @param {*} value value from the view. 39097 * @param {string} trigger Event that triggered the update. 39098 */ 39099 $setViewValue: function(value, trigger) { 39100 this.$viewValue = value; 39101 if (this.$options.getOption('updateOnDefault')) { 39102 this.$$debounceViewValueCommit(trigger); 39103 } 39104 }, 39105 39106 $$debounceViewValueCommit: function(trigger) { 39107 var debounceDelay = this.$options.getOption('debounce'); 39108 39109 if (isNumber(debounceDelay[trigger])) { 39110 debounceDelay = debounceDelay[trigger]; 39111 } else if (isNumber(debounceDelay['default'])) { 39112 debounceDelay = debounceDelay['default']; 39113 } 39114 39115 this.$$timeout.cancel(this.$$pendingDebounce); 39116 var that = this; 39117 if (debounceDelay > 0) { // this fails if debounceDelay is an object 39118 this.$$pendingDebounce = this.$$timeout(function() { 39119 that.$commitViewValue(); 39120 }, debounceDelay); 39121 }
39121 else if (this.$$scope.$root.$$phase) { 39122 this.$commitViewValue(); 39123 } else { 39124 this.$$scope.$apply(function() { 39125 that.$commitViewValue(); 39126 }); 39127 } 39128 }, 39129 39130 /** 39131 * @ngdoc method 39132 * 39133 * @name ngModel.NgModelController#$overrideModelOptions 39134 * 39135 * @description 39136 * 39137 * Override the current model options settings programmatically. 39138 * 39139 * The previous `ModelOptions` value will not be modified. Instead, a 39140 * new `ModelOptions` object will inherit from the previous one overriding 39141 * or inheriting settings that are defined in the given parameter. 39142 * 39143 * See {@link ngModelOptions} for information about what options can be specified 39144 * and how model option inheritance works. 39145 * 39146 * @param {Object} options a hash of settings to override the previous options 39147 * 39148 */ 39149 $overrideModelOptions: function(options) { 39150 this.$options = this.$options.createChild(options); 39151 } 39152}; 39153 39154function setupModelWatcher(ctrl) { 39155 // model -> value 39156 // Note: we cannot use a normal scope.$watch as we want to detect the following: 39157 // 1. scope value is 'a' 39158 // 2. user enters 'b' 39159 // 3. ng-change kicks in and reverts scope value to 'a' 39160 // -> scope value did not change since the last digest as 39161 // ng-change executes in apply phase 39162 // 4. view should be changed back to 'a' 39163 ctrl.$$scope.$watch(function ngModelWatch() { 39164 var modelValue = ctrl.$$ngModelGet(ctrl.$$scope); 39165 39166 // if scope model value and ngModel value are out of sync 39167 // TODO(perf): why not move this to the action fn? 39168 if (modelValue !== ctrl.$modelValue && 39169 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator 39170 // eslint-disable-next-line no-self-compare 39171 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue) 39172 ) { 39173 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; 39174 ctrl.$$parserValid = undefined; 39175 39176 var formatters = ctrl.$formatters, 39177 idx = formatters.length; 39178 39179 var viewValue = modelValue; 39180 while (idx--) { 39181 viewValue = formatters[idx](viewValue); 39182 } 39183 if (ctrl.$viewValue !== viewValue) { 39184 ctrl.$$updateEmptyClasses(viewValue); 39185 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; 39186 ctrl.$render(); 39187 39188 // It is possible that model and view value have been updated during render 39189 ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop); 39190 } 39191 } 39192 39193 return modelValue; 39194 }); 39195} 39196 39197/** 39198 * @ngdoc method 39199 * @name ngModel.NgModelController#$setValidity 39200 * 39201 * @description 39202 * Change the validity state, and notify the form. 39203 * 39204 * This method can be called within $parsers/$formatters or a custom validation implementation. 39205 * However, in most cases it should be sufficient to use the `ngModel.$validators` and 39206 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically. 39207 * 39208 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned 39209 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` 39210 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding. 39211 * The `validationErrorKey` should be in camelCase and will get converted into dash-case 39212 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` 39213 * class and can be bound to as `{{someForm.someControl.$error.myError}}` . 39214 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined), 39215 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`. 39216 * Skipped is used by Angular when validators do not run because of parse errors and 39217 * when `$asyncValidators` do not run because any of the `$validators` failed. 39218 */ 39219addSetValidityMethod({ 39220 clazz: NgModelController, 39221 set: function(object, property) { 39222 object[property] = true; 39223 }, 39224 unset: function(object, property) { 39225 delete object[property]; 39226 } 39227}); 39228 39229 39230/** 39231 * @ngdoc directive 39232 * @name ngModel 39233 * 39234 * @element input 39235 * @priority 1 39236 * 39237 * @description 39238 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a 39239 * property on the scope using {@link ngModel.NgModelController NgModelController}, 39240 * which is created and exposed by this directive. 39241 * 39242 * `ngModel` is responsible for: 39243 * 39244 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select` 39245 * require. 39246 * - Providing validation behavior (i.e. required, number, email, url). 39247 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors). 39248 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, 39249 * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations. 39250 * - Registering the control with its parent {@link ng.directive:form form}. 39251 *
39252 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the 39253 * current scope. If the property doesn't already exist on this scope, it will be created 39254 * implicitly and added to the scope. 39255 * 39256 * For best practices on using `ngModel`, see: 39257 * 39258 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) 39259 * 39260 * For basic examples, how to use `ngModel`, see: 39261 * 39262 * - {@link ng.directive:input input} 39263 * - {@link input[text] text} 39264 * - {@link input[checkbox] checkbox} 39265 * - {@link input[radio] radio} 39266 * - {@link input[number] number} 39267 * - {@link input[email] email} 39268 * - {@link input[url] url} 39269 * - {@link input[date] date} 39270 * - {@link input[datetime-local] datetime-local} 39271 * - {@link input[time] time} 39272 * - {@link input[month] month} 39273 * - {@link input[week] week} 39274 * - {@link ng.directive:select select} 39275 * - {@link ng.directive:textarea textarea} 39276 * 39277 * # Complex Models (objects or collections) 39278 * 39279 * By default, `ngModel` watches the model by reference, not value. This is important to know when 39280 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the 39281 * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered. 39282 * 39283 * The model must be assigned an entirely new object or collection before a re-rendering will occur. 39284 * 39285 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression 39286 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or 39287 * if the select is given the `multiple` attribute. 39288 * 39289 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the 39290 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still 39291 * not trigger a re-rendering of the model. 39292 * 39293 * # CSS classes 39294 * The following CSS classes are added and removed on the associated input/select/textarea element 39295 * depending on the validity of the model. 39296 * 39297 * - `ng-valid`: the model is valid 39298 * - `ng-invalid`: the model is invalid 39299 * - `ng-valid-[key]`: for each valid key added by `$setValidity` 39300 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity` 39301 * - `ng-pristine`: the control hasn't been interacted with yet 39302 * - `ng-dirty`: the control has been interacted with 39303 * - `ng-touched`: the control has been blurred 39304 * - `ng-untouched`: the control hasn't been blurred 39305 * - `ng-pending`: any `$asyncValidators` are unfulfilled 39306 * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined 39307 * by the {@link ngModel.NgModelController#$isEmpty} method 39308 * - `ng-not-empty`: the view contains a non-empty value 39309 * 39310 * Keep in mind that ngAnimate can detect each of these classes when added and removed. 39311 * 39312 * ## Animation Hooks 39313 * 39314 * Animations within models are triggered when any of the associated CSS classes are added and removed 39315 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`, 39316 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself. 39317 * The animations that are triggered within ngModel are similar to how they work in ngClass and 39318 * animations can be hooked into using CSS transitions, keyframes as well as JS animations. 39319 * 39320 * The following example shows a simple way to utilize CSS transitions to style an input element 39321 * that has been rendered as invalid after it has been validated: 39322 * 39323 * <pre> 39324 * //be sure to include ngAnimate as a module to hook into more 39325 * //advanced animations 39326 * .my-input { 39327 * transition:0.5s linear all; 39328 * background: white; 39329 * } 39330 * .my-input.ng-invalid { 39331 * background: red; 39332 * color:white; 39333 * } 39334 * </pre> 39335 * 39336 * @example 39337 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample" name="ng-model"> 39338 <file name="index.html"> 39339 <script> 39340 angular.module('inputExample', [])
39341 .controller('ExampleController', ['$scope', function($scope) { 39342 $scope.val = '1'; 39343 }]); 39344 </script> 39345 <style> 39346 .my-input { 39347 transition:all linear 0.5s; 39348 background: transparent; 39349 } 39350 .my-input.ng-invalid { 39351 color:white; 39352 background: red; 39353 } 39354 </style> 39355 <p id="inputDescription"> 39356 Update input to see transitions when valid/invalid. 39357 Integer is a valid value. 39358 </p> 39359 <form name="testForm" ng-controller="ExampleController"> 39360 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" 39361 aria-describedby="inputDescription" /> 39362 </form> 39363 </file> 39364 * </example> 39365 * 39366 * ## Binding to a getter/setter 39367 * 39368 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a 39369 * function that returns a representation of the model when called with zero arguments, and sets 39370 * the internal state of a model when called with an argument. It's sometimes useful to use this 39371 * for models that have an internal representation that's different from what the model exposes 39372 * to the view. 39373 * 39374 * <div class="alert alert-success"> 39375 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more 39376 * frequently than other parts of your code. 39377 * </div> 39378 * 39379 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that 39380 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to 39381 * a `<form>`, which will enable this behavior for all `<input>`s within it. See 39382 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more. 39383 * 39384 * The following example shows how to use `ngModel` with a getter/setter: 39385 * 39386 * @example 39387 * <example name="ngModel-getter-setter" module="getterSetterExample"> 39388 <file name="index.html">
39389 <div ng-controller="ExampleController"> 39390 <form name="userForm"> 39391 <label>Name: 39392 <input type="text" name="userName" 39393 ng-model="user.name" 39394 ng-model-options="{ getterSetter: true }" /> 39395 </label> 39396 </form> 39397 <pre>user.name = <span ng-bind="user.name()"></span></pre> 39398 </div> 39399 </file> 39400 <file name="app.js"> 39401 angular.module('getterSetterExample', []) 39402 .controller('ExampleController', ['$scope', function($scope) { 39403 var _name = 'Brian'; 39404 $scope.user = { 39405 name: function(newName) { 39406 // Note that newName can be undefined for two reasons: 39407 // 1. Because it is called as a getter and thus called with no arguments 39408 // 2. Because the property should actually be set to undefined. This happens e.g. if the 39409 // input is invalid 39410 return arguments.length ? (_name = newName) : _name; 39411 } 39412 }; 39413 }]); 39414 </file> 39415 * </example> 39416 */ 39417var ngModelDirective = ['$rootScope', function($rootScope) { 39418 return { 39419 restrict: 'A', 39420 require: ['ngModel', '^?form', '^?ngModelOptions'], 39421 controller: NgModelController, 39422 // Prelink needs to run before any input directive 39423 // so that we can set the NgModelOptions in NgModelController 39424 // before anyone else uses it. 39425 priority: 1, 39426 compile: function ngModelCompile(element) { 39427 // Setup initial state of the control 39428 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS); 39429 39430 return { 39431 pre: function ngModelPreLink(scope, element, attr, ctrls) { 39432 var modelCtrl = ctrls[0], 39433 formCtrl = ctrls[1] || modelCtrl.$$parentForm, 39434 optionsCtrl = ctrls[2]; 39435 39436 if (optionsCtrl) { 39437 modelCtrl.$options = optionsCtrl.$options; 39438 } 39439 39440 modelCtrl.$$initGetterSetters(); 39441 39442 // notify others, especially parent forms 39443 formCtrl.$addControl(modelCtrl); 39444 39445 attr.$observe('name', function(newValue) { 39446 if (modelCtrl.$name !== newValue) { 39447 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue); 39448 } 39449 }); 39450 39451 scope.$on('$destroy', function() { 39452 modelCtrl.$$parentForm.$removeControl(modelCtrl); 39453 }); 39454 }, 39455 post: function ngModelPostLink(scope, element, attr, ctrls) { 39456 var modelCtrl = ctrls[0]; 39457 if (modelCtrl.$options.getOption('updateOn')) { 39458 element.on(modelCtrl.$options.getOption('updateOn'), function(ev) { 39459 modelCtrl.$$debounceViewValueCommit(ev && ev.type); 39460 }); 39461 } 39462 39463 function setTouched() { 39464 modelCtrl.$setTouched(); 39465 } 39466 39467 element.on('blur', function() { 39468 if (modelCtrl.$touched) return; 39469 39470 if ($rootScope.$$phase) { 39471 scope.$evalAsync(setTouched); 39472 } else { 39473 scope.$apply(setTouched); 39474 } 39475 }); 39476 } 39477 }; 39478 } 39479 }; 39480}]; 39481 39482/* exported defaultModelOptions */ 39483var defaultModelOptions; 39484var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; 39485 39486/** 39487 * @ngdoc type 39488 * @name ModelOptions 39489 * @description 39490 * A container for the options set by the {@link ngModelOptions} directive 39491 */ 39492function ModelOptions(options) { 39493 this.$$options = options; 39494} 39495 39496ModelOptions.prototype = { 39497 39498 /** 39499 * @ngdoc method 39500 * @name ModelOptions#getOption 39501 * @param {string} name the name of the option to retrieve 39502 * @returns {*} the value of the option 39503 * @description 39504 * Returns the value of the given option 39505 */ 39506 getOption: function(name) { 39507 return this.$$options[name]; 39508 }, 39509 39510 /** 39511 * @ngdoc method 39512 * @name ModelOptions#createChild 39513 * @param {Object} options a hash of options for the new child that will override the parent's options 39514 * @return {ModelOptions} a new `ModelOptions` object initialized with the given options. 39515 */ 39516 createChild: function(options) { 39517 var inheritAll = false; 39518 39519 // make a shallow copy 39520 options = extend({}, options); 39521 39522 // Inherit options from the parent if specified by the value `"$inherit"`
vendor: 5,565 bytes, lines 39523-39662
39523 forEach(options, /* @this */ function(option, key) { 39524 if (option === '$inherit') { 39525 if (key === '*') { 39526 inheritAll = true; 39527 } else { 39528 options[key] = this.$$options[key]; 39529 // `updateOn` is special so we must also inherit the `updateOnDefault` option 39530 if (key === 'updateOn') { 39531 options.updateOnDefault = this.$$options.updateOnDefault; 39532 } 39533 } 39534 } else { 39535 if (key === 'updateOn') { 39536 // If the `updateOn` property contains the `default` event then we have to remove 39537 // it from the event list and set the `updateOnDefault` flag. 39538 options.updateOnDefault = false; 39539 options[key] = trim(option.replace(DEFAULT_REGEXP, function() { 39540 options.updateOnDefault = true; 39541 return ' '; 39542 })); 39543 } 39544 } 39545 }, this); 39546 39547 if (inheritAll) { 39548 // We have a property of the form: `"*": "$inherit"` 39549 delete options['*']; 39550 defaults(options, this.$$options); 39551 } 39552 39553 // Finally add in any missing defaults 39554 defaults(options, defaultModelOptions.$$options); 39555 39556 return new ModelOptions(options); 39557 } 39558}; 39559 39560 39561defaultModelOptions = new ModelOptions({ 39562 updateOn: '', 39563 updateOnDefault: true, 39564 debounce: 0, 39565 getterSetter: false, 39566 allowInvalid: false, 39567 timezone: null 39568}); 39569 39570 39571/** 39572 * @ngdoc directive 39573 * @name ngModelOptions 39574 * 39575 * @description 39576 * This directive allows you to modify the behaviour of {@link ngModel} directives within your 39577 * application. You can specify an `ngModelOptions` directive on any element. All {@link ngModel} 39578 * directives will use the options of their nearest `ngModelOptions` ancestor. 39579 * 39580 * The `ngModelOptions` settings are found by evaluating the value of the attribute directive as 39581 * an Angular expression. This expression should evaluate to an object, whose properties contain 39582 * the settings. For example: `<div "ng-model-options"="{ debounce: 100 }"`. 39583 * 39584 * ## Inheriting Options 39585 * 39586 * You can specify that an `ngModelOptions` setting should be inherited from a parent `ngModelOptions` 39587 * directive by giving it the value of `"$inherit"`. 39588 * Then it will inherit that setting from the first `ngModelOptions` directive found by traversing up the 39589 * DOM tree. If there is no ancestor element containing an `ngModelOptions` directive then default settings 39590 * will be used. 39591 * 39592 * For example given the following fragment of HTML 39593 * 39594 * 39595 * ```html 39596 * <div ng-model-options="{ allowInvalid: true, debounce: 200 }"> 39597 * <form ng-model-options="{ updateOn: 'blur', allowInvalid: '$inherit' }"> 39598 * <input ng-model-options="{ updateOn: 'default', allowInvalid: '$inherit' }" /> 39599 * </form> 39600 * </div> 39601 * ``` 39602 * 39603 * the `input` element will have the following settings 39604 * 39605 * ```js 39606 * { allowInvalid: true, updateOn: 'default', debounce: 0 } 39607 * ``` 39608 * 39609 * Notice that the `debounce` setting was not inherited and used the default value instead. 39610 * 39611 * You can specify that all undefined settings are automatically inherited from an ancestor by 39612 * including a property with key of `"*"` and value of `"$inherit"`. 39613 * 39614 * For example given the following fragment of HTML 39615 * 39616 * 39617 * ```html 39618 * <div ng-model-options="{ allowInvalid: true, debounce: 200 }"> 39619 * <form ng-model-options="{ updateOn: 'blur', "*": '$inherit' }"> 39620 * <input ng-model-options="{ updateOn: 'default', "*": '$inherit' }" /> 39621 * </form> 39622 * </div> 39623 * ``` 39624 * 39625 * the `input` element will have the following settings 39626 * 39627 * ```js 39628 * { allowInvalid: true, updateOn: 'default', debounce: 200 } 39629 * ``` 39630 * 39631 * Notice that the `debounce` setting now inherits the value from the outer `<div>` element. 39632 * 39633 * If you are creating a reusable component then you should be careful when using `"*": "$inherit"` 39634 * since you may inadvertently inherit a setting in the future that changes the behavior of your component. 39635 * 39636 * 39637 * ## Triggering and debouncing model updates 39638 * 39639 * The `updateOn` and `debounce` properties allow you to specify a custom list of events that will 39640 * trigger a model update and/or a debouncing delay so that the actual update only takes place when 39641 * a timer expires; this timer will be reset after another change takes place. 39642 * 39643 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might 39644 * be different from the value in the actual model. This means that if you update the model you 39645 * should also invoke {@link ngModel.NgModelController#$rollbackViewValue} on the relevant input field in 39646 * order to make sure it is synchronized with the model and that any debounced action is canceled. 39647 * 39648 * The easiest way to reference the control's {@link ngModel.NgModelController#$rollbackViewValue} 39649 * method is by making sure the input is placed inside a form that has a `name` attribute. This is 39650 * important because `form` controllers are published to the related scope under the name in their 39651 * `name` attribute. 39652 * 39653 * Any pending changes will take place immediately when an enclosing form is submitted via the 39654 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` 39655 * to have access to the updated model. 39656 * 39657 * The following example shows how to override immediate updates. Changes on the inputs within the 39658 * form will update the model only when the control loses focus (blur event). If `escape` key is 39659 * pressed while the input field is focused, the value is reset to the value in the current model. 39660 * 39661 * <example name="ngModelOptions-directive-blur" module="optionsExample"> 39662 * <file name="index.html">
39663 * <div ng-controller="ExampleController"> 39664 * <form name="userForm"> 39665 * <label> 39666 * Name: 39667 * <input type="text" name="userName" 39668 * ng-model="user.name" 39669 * ng-model-options="{ updateOn: 'blur' }" 39670 * ng-keyup="cancel($event)" /> 39671 * </label><br /> 39672 * <label> 39673 * Other data: 39674 * <input type="text" ng-model="user.data" /> 39675 * </label><br /> 39676 * </form> 39677 * <pre>user.name = <span ng-bind="user.name"></span></pre> 39678 * </div> 39679 * </file> 39680 * <file name="app.js"> 39681 * angular.module('optionsExample', []) 39682 * .controller('ExampleController', ['$scope', function($scope) { 39683 * $scope.user = { name: 'say', data: '' }; 39684 * 39685 * $scope.cancel = function(e) { 39686 * if (e.keyCode === 27) { 39687 * $scope.userForm.userName.$rollbackViewValue(); 39688 * } 39689 * }; 39690 * }]); 39691 * </file> 39692 * <file name="protractor.js" type="protractor"> 39693 * var model = element(by.binding('user.name')); 39694 * var input = element(by.model('user.name')); 39695 * var other = element(by.model('user.data')); 39696 * 39697 * it('should allow custom events', function() { 39698 * input.sendKeys(' hello'); 39699 * input.click(); 39700 * expect(model.getText()).toEqual('say'); 39701 * other.click(); 39702 * expect(model.getText()).toEqual('say hello'); 39703 * }); 39704 * 39705 * it('should $rollbackViewValue when model changes', function() { 39706 * input.sendKeys(' hello'); 39707 * expect(input.getAttribute('value')).toEqual('say hello'); 39708 * input.sendKeys(protractor.Key.ESCAPE); 39709 * expect(input.getAttribute('value')).toEqual('say'); 39710 * other.click(); 39711 * expect(model.getText()).toEqual('say'); 39712 * }); 39713 * </file> 39714 * </example> 39715 * 39716 * The next example shows how to debounce model changes. Model will be updated only 1 sec after last change. 39717 * If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty. 39718 * 39719 * <example name="ngModelOptions-directive-debounce" module="optionsExample"> 39720 * <file name="index.html">
39721 * <div ng-controller="ExampleController"> 39722 * <form name="userForm"> 39723 * Name: 39724 * <input type="text" name="userName" 39725 * ng-model="user.name" 39726 * ng-model-options="{ debounce: 1000 }" /> 39727 * <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br /> 39728 * </form> 39729 * <pre>user.name = <span ng-bind="user.name"></span></pre> 39730 * </div> 39731 * </file> 39732 * <file name="app.js"> 39733 * angular.module('optionsExample', []) 39734 * .controller('ExampleController', ['$scope', function($scope) { 39735 * $scope.user = { name: 'say' }; 39736 * }]); 39737 * </file> 39738 * </example> 39739 * 39740 * ## Model updates and validation 39741 * 39742 * The default behaviour in `ngModel` is that the model value is set to `undefined` when the 39743 * validation determines that the value is invalid. By setting the `allowInvalid` property to true, 39744 * the model will still be updated even if the value is invalid. 39745 * 39746 * 39747 * ## Connecting to the scope 39748 * 39749 * By setting the `getterSetter` property to true you are telling ngModel that the `ngModel` expression 39750 * on the scope refers to a "getter/setter" function rather than the value itself. 39751 * 39752 * The following example shows how to bind to getter/setters: 39753 * 39754 * <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample"> 39755 * <file name="index.html">
39756 * <div ng-controller="ExampleController"> 39757 * <form name="userForm"> 39758 * <label> 39759 * Name: 39760 * <input type="text" name="userName" 39761 * ng-model="user.name" 39762 * ng-model-options="{ getterSetter: true }" /> 39763 * </label> 39764 * </form> 39765 * <pre>user.name = <span ng-bind="user.name()"></span></pre> 39766 * </div> 39767 * </file> 39768 * <file name="app.js"> 39769 * angular.module('getterSetterExample', []) 39770 * .controller('ExampleController', ['$scope', function($scope) { 39771 * var _name = 'Brian'; 39772 * $scope.user = { 39773 * name: function(newName) { 39774 * return angular.isDefined(newName) ? (_name = newName) : _name; 39775 * } 39776 * }; 39777 * }]); 39778 * </file> 39779 * </example> 39780 * 39781 * 39782 * ## Specifying timezones 39783 * 39784 * You can specify the timezone that date/time input directives expect by providing its name in the 39785 * `timezone` property. 39786 * 39787 * @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and 39788 * and its descendents. Valid keys are: 39789 * - `updateOn`: string specifying which event should the input be bound to. You can set several 39790 * events using an space delimited list. There is a special event called `default` that 39791 * matches the default events belonging to the control. 39792 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A 39793 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a 39794 * custom value for each event. For example: 39795 * ``` 39796 * ng-model-options="{ 39797 * updateOn: 'default blur', 39798 * debounce: { 'default': 500, 'blur': 0 } 39799 * }" 39800 * ``` 39801 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did 39802 * not validate correctly instead of the default behavior of setting the model to undefined. 39803 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to 39804 * `ngModel` as getters/setters. 39805 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for 39806 * `<input type="date" />`, `<input type="time" />`, ... . It understands UTC/GMT and the 39807 * continental US time zone abbreviations, but for general use, use a time zone offset, for 39808 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) 39809 * If not specified, the timezone of the browser will be used. 39810 * 39811 */ 39812var ngModelOptionsDirective = function() { 39813 NgModelOptionsController.$inject = ['$attrs', '$scope']; 39814 function NgModelOptionsController($attrs, $scope) { 39815 this.$$attrs = $attrs; 39816 this.$$scope = $scope; 39817 } 39818 NgModelOptionsController.prototype = { 39819 $onInit: function() { 39820 var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions; 39821 var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions); 39822 39823 this.$options = parentOptions.createChild(modelOptionsDefinition); 39824 } 39825 }; 39826 39827 return { 39828 restrict: 'A', 39829 // ngModelOptions needs to run before ngModel and input directives 39830 priority: 10, 39831 require: {parentCtrl: '?^^ngModelOptions'}, 39832 bindToController: true, 39833 controller: NgModelOptionsController 39834 }; 39835}; 39836 39837 39838// shallow copy over values from `src` that are not already specified on `dst` 39839function defaults(dst, src) { 39840 forEach(src, function(value, key) { 39841 if (!isDefined(dst[key])) { 39842 dst[key] = value; 39843 } 39844 }); 39845} 39846 39847/** 39848 * @ngdoc directive 39849 * @name ngNonBindable 39850 * @restrict AC 39851 * @priority 1000 39852 * 39853 * @description 39854 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current 39855 * DOM element. This is useful if the element contains what appears to be Angular directives and 39856 * bindings but which should be ignored by Angular. This could be the case if you have a site that 39857 * displays snippets of code, for instance. 39858 * 39859 * @element ANY 39860 * 39861 * @example 39862 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, 39863 * but the one wrapped in `ngNonBindable` is left alone. 39864 * 39865 * @example 39866 <example name="ng-non-bindable"> 39867 <file name="index.html"> 39868 <div>Normal: {{1 + 2}}</div> 39869 <div ng-non-bindable>Ignored: {{1 + 2}}</div> 39870 </file> 39871 <file name="protractor.js" type="protractor"> 39872 it('should check ng-non-bindable', function() { 39873 expect(element(by.binding('1 + 2')).getText()).toContain('3');
39874 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); 39875 }); 39876 </file> 39877
vendor: 18,942 bytes, lines 39877-40288
39877 </example> 39878 */ 39879var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); 39880 39881/* exported ngOptionsDirective */ 39882 39883/* global jqLiteRemove */ 39884 39885var ngOptionsMinErr = minErr('ngOptions'); 39886 39887/** 39888 * @ngdoc directive 39889 * @name ngOptions 39890 * @restrict A 39891 * 39892 * @description 39893 * 39894 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>` 39895 * elements for the `<select>` element using the array or object obtained by evaluating the 39896 * `ngOptions` comprehension expression. 39897 * 39898 * In many cases, {@link ng.directive:ngRepeat ngRepeat} can be used on `<option>` elements instead of 39899 * `ngOptions` to achieve a similar result. However, `ngOptions` provides some benefits: 39900 * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the 39901 * comprehension expression 39902 * - reduced memory consumption by not creating a new scope for each repeated instance 39903 * - increased render speed by creating the options in a documentFragment instead of individually 39904 * 39905 * When an item in the `<select>` menu is selected, the array element or object property 39906 * represented by the selected option will be bound to the model identified by the `ngModel` 39907 * directive. 39908 * 39909 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can 39910 * be nested into the `<select>` element. This element will then represent the `null` or "not selected" 39911 * option. See example below for demonstration. 39912 * 39913 * ## Complex Models (objects or collections) 39914 * 39915 * By default, `ngModel` watches the model by reference, not value. This is important to know when 39916 * binding the select to a model that is an object or a collection. 39917 * 39918 * One issue occurs if you want to preselect an option. For example, if you set 39919 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection, 39920 * because the objects are not identical. So by default, you should always reference the item in your collection 39921 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`. 39922 * 39923 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity 39924 * of the item not by reference, but by the result of the `track by` expression. For example, if your 39925 * collection items have an id property, you would `track by item.id`. 39926 * 39927 * A different issue with objects or collections is that ngModel won't detect if an object property or 39928 * a collection item changes. For that reason, `ngOptions` additionally watches the model using 39929 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute. 39930 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection 39931 * has not changed identity, but only a property on the object or an item in the collection changes. 39932 * 39933 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection 39934 * if the model is an array). This means that changing a property deeper than the first level inside the 39935 * object/collection will not trigger a re-rendering. 39936 * 39937 * ## `select` **`as`** 39938 * 39939 * Using `select` **`as`** will bind the result of the `select` expression to the model, but 39940 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources) 39941 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression 39942 * is used, the result of that expression will be set as the value of the `option` and `select` elements. 39943 * 39944 * 39945 * ### `select` **`as`** and **`track by`** 39946 * 39947 * <div class="alert alert-warning"> 39948 * Be careful when using `select` **`as`** and **`track by`** in the same expression. 39949 * </div> 39950 * 39951 * Given this array of items on the $scope: 39952 * 39953 * ```js 39954 * $scope.items = [{ 39955 * id: 1, 39956 * label: 'aLabel', 39957 * subItem: { name: 'aSubItem' } 39958 * }, { 39959 * id: 2, 39960 * label: 'bLabel', 39961 * subItem: { name: 'bSubItem' } 39962 * }]; 39963 * ``` 39964 * 39965 * This will work: 39966 * 39967 * ```html 39968 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select> 39969 * ``` 39970 * ```js 39971 * $scope.selected = $scope.items[0]; 39972 * ``` 39973 * 39974 * but this will not work: 39975 * 39976 * ```html 39977 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select> 39978 * ``` 39979 * ```js 39980 * $scope.selected = $scope.items[0].subItem; 39981 * ``` 39982 * 39983 * In both examples, the **`track by`** expression is applied successfully to each `item` in the 39984 * `items` array. Because the selected option has been set programmatically in the controller, the 39985 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the 39986 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with 39987 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`** 39988 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value 39989 * is not matched against any `<option>` and the `<select>` appears as having no selected value. 39990 * 39991 * 39992 * @param {string} ngModel Assignable AngularJS expression to data-bind to. 39993 * @param {comprehension_expression} ngOptions in one of the following forms: 39994 * 39995 * * for array data sources: 39996 * * `label` **`for`** `value` **`in`** `array` 39997 * * `select` **`as`** `label` **`for`** `value` **`in`** `array` 39998 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` 39999 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` 40000 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` 40001 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` 40002 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr` 40003 * (for including a filter with `track by`) 40004 * * for object data sources: 40005 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` 40006 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` 40007 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` 40008 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object` 40009 * * `select` **`as`** `label` **`group by`** `group` 40010 * **`for` `(`**`key`**`,`** `value`**`) in`** `object` 40011 * * `select` **`as`** `label` **`disable when`** `disable` 40012 * **`for` `(`**`key`**`,`** `value`**`) in`** `object` 40013 * 40014 * Where: 40015 * 40016 * * `array` / `object`: an expression which evaluates to an array / object to iterate over. 40017 * * `value`: local variable which will refer to each item in the `array` or each property value 40018 * of `object` during iteration. 40019 * * `key`: local variable which will refer to a property name in `object` during iteration. 40020 * * `label`: The result of this expression will be the label for `<option>` element. The 40021 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`). 40022 * * `select`: The result of this expression will be bound to the model of the parent `<select>` 40023 * element. If not specified, `select` expression will default to `value`. 40024 * * `group`: The result of this expression will be used to group options using the `<optgroup>` 40025 * DOM element. 40026 * * `disable`: The result of this expression will be used to disable the rendered `<option>` 40027 * element. Return `true` to disable. 40028 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be 40029 * used to identify the objects in the array. The `trackexpr` will most likely refer to the 40030 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved 40031 * even when the options are recreated (e.g. reloaded from the server). 40032 * @param {string=} name Property name of the form under which the control is published. 40033 * @param {string=} required The control is considered valid only if value is entered. 40034 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 40035 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 40036 * `required` when you want to data-bind to the `required` attribute. 40037 * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the 40038 * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive. 40039 * 40040 * @example 40041 <example module="selectExample" name="select"> 40042 <file name="index.html"> 40043 <script> 40044 angular.module('selectExample', []) 40045 .controller('ExampleController', ['$scope', function($scope) { 40046 $scope.colors = [ 40047 {name:'black', shade:'dark'}, 40048 {name:'white', shade:'light', notAnOption: true}, 40049 {name:'red', shade:'dark'}, 40050 {name:'blue', shade:'dark', notAnOption: true}, 40051 {name:'yellow', shade:'light', notAnOption: false} 40052 ]; 40053 $scope.myColor = $scope.colors[2]; // red 40054 }]); 40055 </script> 40056 <div ng-controller="ExampleController"> 40057 <ul> 40058 <li ng-repeat="color in colors"> 40059 <label>Name: <input ng-model="color.name"></label> 40060 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label> 40061 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button> 40062 </li> 40063 <li> 40064 <button ng-click="colors.push({})">add</button> 40065 </li> 40066 </ul> 40067 <hr/> 40068 <label>Color (null not allowed): 40069 <select ng-model="myColor" ng-options="color.name for color in colors"></select> 40070 </label><br/> 40071 <label>Color (null allowed): 40072 <span class="nullable"> 40073 <select ng-model="myColor" ng-options="color.name for color in colors"> 40074 <option value="">-- choose color --</option> 40075 </select> 40076 </span></label><br/> 40077 40078 <label>Color grouped by shade: 40079 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors"> 40080 </select> 40081 </label><br/> 40082 40083 <label>Color grouped by shade, with some disabled: 40084 <select ng-model="myColor" 40085 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors"> 40086 </select> 40087 </label><br/> 40088 40089 40090 40091 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>. 40092 <br/> 40093 <hr/> 40094 Currently selected: {{ {selected_color:myColor} }} 40095 <div style="border:solid 1px black; height:20px" 40096 ng-style="{'background-color':myColor.name}"> 40097 </div> 40098 </div> 40099 </file> 40100 <file name="protractor.js" type="protractor"> 40101 it('should check ng-options', function() { 40102 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red'); 40103 element.all(by.model('myColor')).first().click(); 40104 element.all(by.css('select[ng-model="myColor"] option')).first().click(); 40105 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black'); 40106 element(by.css('.nullable select[ng-model="myColor"]')).click(); 40107 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click(); 40108 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null'); 40109 }); 40110 </file> 40111 </example> 40112 */ 40113 40114/* eslint-disable max-len */ 40115// //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555000000000666666666666600000007777777777777000000000000000888888888800000000000000000009999999999 40116var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([$\w][$\w]*)|(?:\(\s*([$\w][$\w]*)\s*,\s*([$\w][$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/; 40117 // 1: value expression (valueFn) 40118 // 2: label expression (displayFn) 40119 // 3: group by expression (groupByFn) 40120 // 4: disable when expression (disableWhenFn) 40121 // 5: array item variable name 40122 // 6: object item key variable name 40123 // 7: object item value variable name 40124 // 8: collection expression 40125 // 9: track by expression 40126/* eslint-enable */ 40127 40128 40129var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) { 40130 40131 function parseOptionsExpression(optionsExp, selectElement, scope) { 40132 40133 var match = optionsExp.match(NG_OPTIONS_REGEXP); 40134 if (!(match)) { 40135 throw ngOptionsMinErr('iexp', 40136 'Expected expression in form of ' + 40137 '\'_select_ (as _label_)? for (_key_,)?_value_ in _collection_\'' + 40138 ' but got \'{0}\'. Element: {1}', 40139 optionsExp, startingTag(selectElement)); 40140 } 40141 40142 // Extract the parts from the ngOptions expression 40143 40144 // The variable name for the value of the item in the collection 40145 var valueName = match[5] || match[7]; 40146 // The variable name for the key of the item in the collection 40147 var keyName = match[6]; 40148 40149 // An expression that generates the viewValue for an option if there is a label expression 40150 var selectAs = / as /.test(match[0]) && match[1]; 40151 // An expression that is used to track the id of each object in the options collection 40152 var trackBy = match[9]; 40153 // An expression that generates the viewValue for an option if there is no label expression 40154 var valueFn = $parse(match[2] ? match[1] : valueName); 40155 var selectAsFn = selectAs && $parse(selectAs); 40156 var viewValueFn = selectAsFn || valueFn; 40157 var trackByFn = trackBy && $parse(trackBy); 40158 40159 // Get the value by which we are going to track the option 40160 // if we have a trackFn then use that (passing scope and locals) 40161 // otherwise just hash the given viewValue 40162 var getTrackByValueFn = trackBy ? 40163 function(value, locals) { return trackByFn(scope, locals); } : 40164 function getHashOfValue(value) { return hashKey(value); }; 40165 var getTrackByValue = function(value, key) { 40166 return getTrackByValueFn(value, getLocals(value, key)); 40167 }; 40168 40169 var displayFn = $parse(match[2] || match[1]); 40170 var groupByFn = $parse(match[3] || ''); 40171 var disableWhenFn = $parse(match[4] || ''); 40172 var valuesFn = $parse(match[8]); 40173 40174 var locals = {}; 40175 var getLocals = keyName ? function(value, key) { 40176 locals[keyName] = key; 40177 locals[valueName] = value; 40178 return locals; 40179 } : function(value) { 40180 locals[valueName] = value; 40181 return locals; 40182 }; 40183 40184 40185 function Option(selectValue, viewValue, label, group, disabled) { 40186 this.selectValue = selectValue; 40187 this.viewValue = viewValue; 40188 this.label = label; 40189 this.group = group; 40190 this.disabled = disabled; 40191 } 40192 40193 function getOptionValuesKeys(optionValues) { 40194 var optionValuesKeys; 40195 40196 if (!keyName && isArrayLike(optionValues)) { 40197 optionValuesKeys = optionValues; 40198 } else { 40199 // if object, extract keys, in enumeration order, unsorted 40200 optionValuesKeys = []; 40201 for (var itemKey in optionValues) { 40202 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') { 40203 optionValuesKeys.push(itemKey); 40204 } 40205 } 40206 } 40207 return optionValuesKeys; 40208 } 40209 40210 return { 40211 trackBy: trackBy, 40212 getTrackByValue: getTrackByValue, 40213 getWatchables: $parse(valuesFn, function(optionValues) { 40214 // Create a collection of things that we would like to watch (watchedArray) 40215 // so that they can all be watched using a single $watchCollection 40216 // that only runs the handler once if anything changes 40217 var watchedArray = []; 40218 optionValues = optionValues || []; 40219 40220 var optionValuesKeys = getOptionValuesKeys(optionValues); 40221 var optionValuesLength = optionValuesKeys.length; 40222 for (var index = 0; index < optionValuesLength; index++) { 40223 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index]; 40224 var value = optionValues[key]; 40225 40226 var locals = getLocals(value, key); 40227 var selectValue = getTrackByValueFn(value, locals); 40228 watchedArray.push(selectValue); 40229 40230 // Only need to watch the displayFn if there is a specific label expression 40231 if (match[2] || match[1]) { 40232 var label = displayFn(scope, locals); 40233 watchedArray.push(label); 40234 } 40235 40236 // Only need to watch the disableWhenFn if there is a specific disable expression 40237 if (match[4]) { 40238 var disableWhen = disableWhenFn(scope, locals); 40239 watchedArray.push(disableWhen); 40240 } 40241 } 40242 return watchedArray; 40243 }), 40244 40245 getOptions: function() { 40246 40247 var optionItems = []; 40248 var selectValueMap = {}; 40249 40250 // The option values were already computed in the `getWatchables` fn, 40251 // which must have been called to trigger `getOptions` 40252 var optionValues = valuesFn(scope) || []; 40253 var optionValuesKeys = getOptionValuesKeys(optionValues); 40254 var optionValuesLength = optionValuesKeys.length; 40255 40256 for (var index = 0; index < optionValuesLength; index++) { 40257 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index]; 40258 var value = optionValues[key]; 40259 var locals = getLocals(value, key); 40260 var viewValue = viewValueFn(scope, locals); 40261 var selectValue = getTrackByValueFn(viewValue, locals); 40262 var label = displayFn(scope, locals); 40263 var group = groupByFn(scope, locals); 40264 var disabled = disableWhenFn(scope, locals); 40265 var optionItem = new Option(selectValue, viewValue, label, group, disabled); 40266 40267 optionItems.push(optionItem); 40268 selectValueMap[selectValue] = optionItem; 40269 } 40270 40271 return { 40272 items: optionItems, 40273 selectValueMap: selectValueMap, 40274 getOptionFromViewValue: function(value) { 40275 return selectValueMap[getTrackByValue(value)]; 40276 }, 40277 getViewValueFromOption: function(option) { 40278 // If the viewValue could be an object that may be mutated by the application, 40279 // we need to make a copy and not return the reference to the value on the option. 40280 return trackBy ? copy(option.viewValue) : option.viewValue; 40281 } 40282 }; 40283 } 40284 }; 40285 } 40286 40287 40288 //
40288we can't just jqLite('<option>') since jqLite is not smart enough 40289 // to create it in <select> and IE barfs otherwise. 40290 var optionTemplate = window.document.createElement('option'), 40291 optGroupTemplate = window.document.createElement('optgroup'); 40292 40293 function ngOptionsPostLink(scope, selectElement, attr, ctrls) { 40294 40295 var selectCtrl = ctrls[0]; 40296 var ngModelCtrl = ctrls[1]; 40297 var multiple = attr.multiple; 40298 40299 // The emptyOption allows the application developer to provide their own custom "empty" 40300 // option when the viewValue does not match any of the option values. 40301 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) { 40302 if (children[i].value === '') { 40303 selectCtrl.hasEmptyOption = true; 40304 selectCtrl.emptyOption = children.eq(i); 40305 break; 40306 } 40307 } 40308 40309 var providedEmptyOption = !!selectCtrl.emptyOption; 40310 40311 var unknownOption = jqLite(optionTemplate.cloneNode(false)); 40312 unknownOption.val('?'); 40313 40314 var options; 40315 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope); 40316 // This stores the newly created options before they are appended to the select. 40317 // Since the contents are removed from the fragment when it is appended, 40318 // we only need to create it once. 40319 var listFragment = $document[0].createDocumentFragment(); 40320 40321 // Overwrite the implementation. ngOptions doesn't use hashes 40322 selectCtrl.generateUnknownOptionValue = function(val) { 40323 return '?'; 40324 }; 40325 40326 // Update the controller methods for multiple selectable options 40327 if (!multiple) { 40328 40329 selectCtrl.writeValue = function writeNgOptionsValue(value) { 40330 var selectedOption = options.selectValueMap[selectElement.val()]; 40331 var option = options.getOptionFromViewValue(value); 40332 40333 // Make sure to remove the selected attribute from the previously selected option 40334 // Otherwise, screen readers might get confused 40335 if (selectedOption) selectedOption.element.removeAttribute('selected'); 40336 40337 if (option) { 40338 // Don't update the option when it is already selected. 40339 // For example, the browser will select the first option by default. In that case, 40340 // most properties are set automatically - except the `selected` attribute, which we 40341 // set always 40342 40343 if (selectElement[0].value !== option.selectValue) { 40344 selectCtrl.removeUnknownOption(); 40345 selectCtrl.unselectEmptyOption(); 40346 40347 selectElement[0].value = option.selectValue; 40348 option.element.selected = true; 40349 } 40350 40351 option.element.setAttribute('selected', 'selected'); 40352 } else { 40353 40354 if (providedEmptyOption) { 40355 selectCtrl.selectEmptyOption(); 40356 } else if (selectCtrl.unknownOption.parent().length) { 40357 selectCtrl.updateUnknownOption(value); 40358 } else { 40359 selectCtrl.renderUnknownOption(value); 40360 } 40361 } 40362 }; 40363 40364 selectCtrl.readValue = function readNgOptionsValue() { 40365 40366 var selectedOption = options.selectValueMap[selectElement.val()]; 40367 40368 if (selectedOption && !selectedOption.disabled) { 40369 selectCtrl.unselectEmptyOption(); 40370 selectCtrl.removeUnknownOption(); 40371 return options.getViewValueFromOption(selectedOption); 40372 } 40373 return null; 40374 }; 40375 40376 // If we are using `track by` then we must watch the tracked value on the model 40377 // since ngModel only watches for object identity change
vendor: 10,731 bytes, lines 40378-40656
40378 // FIXME: When a user selects an option, this watch will fire needlessly 40379 if (ngOptions.trackBy) { 40380 scope.$watch( 40381 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); }, 40382 function() { ngModelCtrl.$render(); } 40383 ); 40384 } 40385 40386 } else { 40387 40388 selectCtrl.writeValue = function writeNgOptionsMultiple(values) { 40389 // Only set `<option>.selected` if necessary, in order to prevent some browsers from 40390 // scrolling to `<option>` elements that are outside the `<select>` element's viewport. 40391 40392 var selectedOptions = values && values.map(getAndUpdateSelectedOption) || []; 40393 40394 options.items.forEach(function(option) { 40395 if (option.element.selected && !includes(selectedOptions, option)) { 40396 option.element.selected = false; 40397 } 40398 }); 40399 }; 40400 40401 40402 selectCtrl.readValue = function readNgOptionsMultiple() { 40403 var selectedValues = selectElement.val() || [], 40404 selections = []; 40405 40406 forEach(selectedValues, function(value) { 40407 var option = options.selectValueMap[value]; 40408 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option)); 40409 }); 40410 40411 return selections; 40412 }; 40413 40414 // If we are using `track by` then we must watch these tracked values on the model 40415 // since ngModel only watches for object identity change 40416 if (ngOptions.trackBy) { 40417 40418 scope.$watchCollection(function() { 40419 if (isArray(ngModelCtrl.$viewValue)) { 40420 return ngModelCtrl.$viewValue.map(function(value) { 40421 return ngOptions.getTrackByValue(value); 40422 }); 40423 } 40424 }, function() { 40425 ngModelCtrl.$render(); 40426 }); 40427 40428 } 40429 } 40430 40431 if (providedEmptyOption) { 40432 40433 // we need to remove it before calling selectElement.empty() because otherwise IE will 40434 // remove the label from the element. wtf? 40435 selectCtrl.emptyOption.remove(); 40436 40437 // compile the element since there might be bindings in it 40438 $compile(selectCtrl.emptyOption)(scope); 40439 40440 if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) { 40441 // This means the empty option has currently no actual DOM node, probably because 40442 // it has been modified by a transclusion directive. 40443 selectCtrl.hasEmptyOption = false; 40444 40445 // Redefine the registerOption function, which will catch 40446 // options that are added by ngIf etc. (rendering of the node is async because of 40447 // lazy transclusion) 40448 selectCtrl.registerOption = function(optionScope, optionEl) { 40449 if (optionEl.val() === '') { 40450 selectCtrl.hasEmptyOption = true; 40451 selectCtrl.emptyOption = optionEl; 40452 selectCtrl.emptyOption.removeClass('ng-scope'); 40453 // This ensures the new empty option is selected if previously no option was selected 40454 ngModelCtrl.$render(); 40455 40456 optionEl.on('$destroy', function() { 40457 selectCtrl.hasEmptyOption = false; 40458 selectCtrl.emptyOption = undefined; 40459 }); 40460 } 40461 }; 40462 40463 } else { 40464 // remove the class, which is added automatically because we recompile the element and it 40465 // becomes the compilation root 40466 selectCtrl.emptyOption.removeClass('ng-scope'); 40467 } 40468 40469 } 40470 40471 selectElement.empty(); 40472 40473 // We need to do this here to ensure that the options object is defined 40474 // when we first hit it in writeNgOptionsValue 40475 updateOptions(); 40476 40477 // We will re-render the option elements if the option values or labels change 40478 scope.$watchCollection(ngOptions.getWatchables, updateOptions); 40479 40480 // ------------------------------------------------------------------ // 40481 40482 function addOptionElement(option, parent) { 40483 var optionElement = optionTemplate.cloneNode(false); 40484 parent.appendChild(optionElement); 40485 updateOptionElement(option, optionElement); 40486 } 40487 40488 function getAndUpdateSelectedOption(viewValue) { 40489 var option = options.getOptionFromViewValue(viewValue); 40490 var element = option && option.element; 40491 40492 if (element && !element.selected) element.selected = true; 40493 40494 return option; 40495 } 40496 40497 function updateOptionElement(option, element) { 40498 option.element = element; 40499 element.disabled = option.disabled; 40500 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive 40501 // selects in certain circumstances when multiple selects are next to each other and display 40502 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size]. 40503 // See https://github.com/angular/angular.js/issues/11314 for more info. 40504 // This is unfortunately untestable with unit / e2e tests 40505 if (option.label !== element.label) { 40506 element.label = option.label; 40507 element.textContent = option.label; 40508 } 40509 element.value = option.selectValue; 40510 } 40511 40512 function updateOptions() { 40513 var previousValue = options && selectCtrl.readValue(); 40514 40515 // We must remove all current options, but cannot simply set innerHTML = null 40516 // since the providedEmptyOption might have an ngIf on it that inserts comments which we 40517 // must preserve. 40518 // Instead, iterate over the current option elements and remove them or their optgroup 40519 // parents 40520 if (options) { 40521 40522 for (var i = options.items.length - 1; i >= 0; i--) { 40523 var option = options.items[i]; 40524 if (isDefined(option.group)) { 40525 jqLiteRemove(option.element.parentNode); 40526 } else { 40527 jqLiteRemove(option.element); 40528 } 40529 } 40530 } 40531 40532 options = ngOptions.getOptions(); 40533 40534 var groupElementMap = {}; 40535 40536 // Ensure that the empty option is always there if it was explicitly provided 40537 if (providedEmptyOption) { 40538 selectElement.prepend(selectCtrl.emptyOption); 40539 } 40540 40541 options.items.forEach(function addOption(option) { 40542 var groupElement; 40543 40544 if (isDefined(option.group)) { 40545 40546 // This option is to live in a group 40547 // See if we have already created this group 40548 groupElement = groupElementMap[option.group]; 40549 40550 if (!groupElement) { 40551 40552 groupElement = optGroupTemplate.cloneNode(false); 40553 listFragment.appendChild(groupElement); 40554 40555 // Update the label on the group element 40556 // "null" is special cased because of Safari 40557 groupElement.label = option.group === null ? 'null' : option.group; 40558 40559 // Store it for use later 40560 groupElementMap[option.group] = groupElement; 40561 } 40562 40563 addOptionElement(option, groupElement); 40564 40565 } else { 40566 40567 // This option is not in a group 40568 addOptionElement(option, listFragment); 40569 } 40570 }); 40571 40572 selectElement[0].appendChild(listFragment); 40573 40574 ngModelCtrl.$render(); 40575 40576 // Check to see if the value has changed due to the update to the options 40577 if (!ngModelCtrl.$isEmpty(previousValue)) { 40578 var nextValue = selectCtrl.readValue(); 40579 var isNotPrimitive = ngOptions.trackBy || multiple; 40580 if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) { 40581 ngModelCtrl.$setViewValue(nextValue); 40582 ngModelCtrl.$render(); 40583 } 40584 } 40585 40586 } 40587 } 40588 40589 return { 40590 restrict: 'A', 40591 terminal: true, 40592 require: ['select', 'ngModel'], 40593 link: { 40594 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) { 40595 // Deactivate the SelectController.register method to prevent 40596 // option directives from accidentally registering themselves 40597 // (and unwanted $destroy handlers etc.) 40598 ctrls[0].registerOption = noop; 40599 }, 40600 post: ngOptionsPostLink 40601 } 40602 }; 40603}]; 40604 40605/** 40606 * @ngdoc directive 40607 * @name ngPluralize 40608 * @restrict EA 40609 * 40610 * @description 40611 * `ngPluralize` is a directive that displays messages according to en-US localization rules. 40612 * These rules are bundled with angular.js, but can be overridden 40613 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive 40614 * by specifying the mappings between 40615 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) 40616 * and the strings to be displayed. 40617 * 40618 * # Plural categories and explicit number rules 40619 * There are two 40620 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) 40621 * in Angular's default en-US locale: "one" and "other". 40622 * 40623 * While a plural category may match many numbers (for example, in en-US locale, "other" can match 40624 * any number that is not 1), an explicit number rule can only match one number. For example, the 40625 * explicit number rule for "3" matches the number 3. There are examples of plural categories 40626 * and explicit number rules throughout the rest of this documentation. 40627 * 40628 * # Configuring ngPluralize 40629 * You configure ngPluralize by providing 2 attributes: `count` and `when`. 40630 * You can also provide an optional attribute, `offset`. 40631 * 40632 * The value of the `count` attribute can be either a string or an {@link guide/expression 40633 * Angular expression}; these are evaluated on the current scope for its bound value. 40634 * 40635 * The `when` attribute specifies the mappings between plural categories and the actual 40636 * string to be displayed. The value of the attribute should be a JSON object. 40637 * 40638 * The following example shows how to configure ngPluralize: 40639 * 40640 * ```html 40641 * <ng-pluralize count="personCount" 40642 when="{'0': 'Nobody is viewing.', 40643 * 'one': '1 person is viewing.', 40644 * 'other': '{} people are viewing.'}"> 40645 * </ng-pluralize> 40646 *``` 40647 * 40648 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not 40649 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" 40650 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for 40651 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can 40652 * show "a dozen people are viewing". 40653 * 40654 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted 40655 * into pluralized strings. In the previous example, Angular will replace `{}` with 40656 * <span ng-non-bindable>`{{personCount}}
40656`</span>. The closed braces `{}` is a placeholder 40657 * for <span ng-non-bindable>{{numberExpression}}</span>. 40658 * 40659 * If no rule is defined for a category, then an empty string is displayed and a warning is generated. 40660 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`. 40661 * 40662 * # Configuring ngPluralize with offset 40663 * The `offset` attribute allows further customization of pluralized text, which can result in 40664 * a better user experience. For example, instead of the message "4 people are viewing this document", 40665 * you might display "John, Kate and 2 others are viewing this document". 40666 * The offset attribute allows you to offset a number by any desired value. 40667 * Let's take a look at an example: 40668 * 40669 * ```html 40670 * <ng-pluralize count="personCount" offset=2 40671 * when="{'0': 'Nobody is viewing.', 40672 * '1': '{{person1}} is viewing.', 40673 * '2': '{{person1}} and {{person2}} are viewing.', 40674 * 'one': '{{person1}}, {{person2}} and one other person are viewing.', 40675 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> 40676 * </ng-pluralize> 40677 * ``` 40678 * 40679 * Notice that we are still using two plural categories(one, other), but we added 40680 * three explicit number rules 0, 1 and 2. 40681 * When one person, perhaps John, views the document, "John is viewing" will be shown. 40682 * When three people view the document, no explicit number rule is found, so 40683 * an offset of 2 is taken off 3, and Angular
vendor: 6,786 bytes, lines 40683-40844
40683 uses 1 to decide the plural category. 40684 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing" 40685 * is shown. 40686 * 40687 * Note that when you specify offsets, you must provide explicit number rules for 40688 * numbers from 0 up to and including the offset. If you use an offset of 3, for example, 40689 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for 40690 * plural categories "one" and "other". 40691 * 40692 * @param {string|expression} count The variable to be bound to. 40693 * @param {string} when The mapping between plural category to its corresponding strings. 40694 * @param {number=} offset Offset to deduct from the total number. 40695 * 40696 * @example 40697 <example module="pluralizeExample" name="ng-pluralize"> 40698 <file name="index.html"> 40699 <script> 40700 angular.module('pluralizeExample', []) 40701 .controller('ExampleController', ['$scope', function($scope) { 40702 $scope.person1 = 'Igor'; 40703 $scope.person2 = 'Misko'; 40704 $scope.personCount = 1; 40705 }]); 40706 </script> 40707 <div ng-controller="ExampleController"> 40708 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/> 40709 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/> 40710 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/> 40711 40712 <!--- Example with simple pluralization rules for en locale ---> 40713 Without Offset: 40714 <ng-pluralize count="personCount" 40715 when="{'0': 'Nobody is viewing.', 40716 'one': '1 person is viewing.', 40717 'other': '{} people are viewing.'}"> 40718 </ng-pluralize><br> 40719 40720 <!--- Example with offset ---> 40721 With Offset(2): 40722 <ng-pluralize count="personCount" offset=2 40723 when="{'0': 'Nobody is viewing.', 40724 '1': '{{person1}} is viewing.', 40725 '2': '{{person1}} and {{person2}} are viewing.', 40726 'one': '{{person1}}, {{person2}} and one other person are viewing.', 40727 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> 40728 </ng-pluralize> 40729 </div> 40730 </file> 40731 <file name="protractor.js" type="protractor"> 40732 it('should show correct pluralized string', function() { 40733 var withoutOffset = element.all(by.css('ng-pluralize')).get(0); 40734 var withOffset = element.all(by.css('ng-pluralize')).get(1); 40735 var countInput = element(by.model('personCount')); 40736 40737 expect(withoutOffset.getText()).toEqual('1 person is viewing.'); 40738 expect(withOffset.getText()).toEqual('Igor is viewing.'); 40739 40740 countInput.clear(); 40741 countInput.sendKeys('0'); 40742 40743 expect(withoutOffset.getText()).toEqual('Nobody is viewing.'); 40744 expect(withOffset.getText()).toEqual('Nobody is viewing.'); 40745 40746 countInput.clear(); 40747 countInput.sendKeys('2'); 40748 40749 expect(withoutOffset.getText()).toEqual('2 people are viewing.'); 40750 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.'); 40751 40752 countInput.clear(); 40753 countInput.sendKeys('3'); 40754 40755 expect(withoutOffset.getText()).toEqual('3 people are viewing.'); 40756 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.'); 40757 40758 countInput.clear(); 40759 countInput.sendKeys('4'); 40760 40761 expect(withoutOffset.getText()).toEqual('4 people are viewing.'); 40762 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.'); 40763 }); 40764 it('should show data-bound names', function() { 40765 var withOffset = element.all(by.css('ng-pluralize')).get(1); 40766 var personCount = element(by.model('personCount')); 40767 var person1 = element(by.model('person1')); 40768 var person2 = element(by.model('person2')); 40769 personCount.clear(); 40770 personCount.sendKeys('4'); 40771 person1.clear(); 40772 person1.sendKeys('Di'); 40773 person2.clear(); 40774 person2.sendKeys('Vojta'); 40775 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.'); 40776 }); 40777 </file> 40778 </example> 40779 */ 40780var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) { 40781 var BRACE = /{}/g, 40782 IS_WHEN = /^when(Minus)?(.+)$/; 40783 40784 return { 40785 link: function(scope, element, attr) { 40786 var numberExp = attr.count, 40787 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs 40788 offset = attr.offset || 0, 40789 whens = scope.$eval(whenExp) || {}, 40790 whensExpFns = {}, 40791 startSymbol = $interpolate.startSymbol(), 40792 endSymbol = $interpolate.endSymbol(), 40793 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol, 40794 watchRemover = angular.noop, 40795 lastCount; 40796 40797 forEach(attr, function(expression, attributeName) { 40798 var tmpMatch = IS_WHEN.exec(attributeName); 40799 if (tmpMatch) { 40800 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]); 40801 whens[whenKey] = element.attr(attr.$attr[attributeName]); 40802 } 40803 }); 40804 forEach(whens, function(expression, key) { 40805 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement)); 40806 40807 }); 40808 40809 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) { 40810 var count = parseFloat(newVal); 40811 var countIsNaN = isNumberNaN(count); 40812 40813 if (!countIsNaN && !(count in whens)) { 40814 // If an explicit number rule such as 1, 2, 3... is defined, just use it. 40815 // Otherwise, check it against pluralization rules in $locale service. 40816 count = $locale.pluralCat(count - offset); 40817 } 40818 40819 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch. 40820 // In JS `NaN !== NaN`, so we have to explicitly check. 40821 if ((count !== lastCount) && !(countIsNaN && isNumberNaN(lastCount))) { 40822 watchRemover(); 40823 var whenExpFn = whensExpFns[count]; 40824 if (isUndefined(whenExpFn)) { 40825 if (newVal != null) { 40826 $log.debug('ngPluralize: no rule defined for \'' + count + '\' in ' + whenExp); 40827 } 40828 watchRemover = noop; 40829 updateElementText(); 40830 } else { 40831 watchRemover = scope.$watch(whenExpFn, updateElementText); 40832 } 40833 lastCount = count; 40834 } 40835 }); 40836 40837 function updateElementText(newText) { 40838 element.text(newText || ''); 40839 } 40840 } 40841 }; 40842}]; 40843 40844/*
40844 exported ngRepeatDirective */ 40845 40846/** 40847 * @ngdoc directive 40848 * @name ngRepeat 40849 * @multiElement 40850 * @restrict A 40851 * 40852 * @description 40853 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template 40854 * instance gets its own scope, where the given loop variable is set to the current collection item, 40855 * and `$index` is set to the item index or key. 40856 * 40857 * Special properties are exposed on the local scope of each template instance, including: 40858 * 40859 * | Variable | Type | Details | 40860 * |-----------|-----------------|-----------------------------------------------------------------------------| 40861 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) | 40862 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. | 40863 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. | 40864 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. | 40865 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | 40866 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | 40867 * 40868 * <div class="alert alert-info"> 40869 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}. 40870 * This may be useful when, for instance, nesting ngRepeats. 40871 * </div> 40872 * 40873 * 40874 * # Iterating over object properties 40875 * 40876 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following 40877 * syntax: 40878 * 40879 * ```js 40880 * <div ng-repeat="(key, value) in myObj"> ... </div> 40881 * ``` 40882 * 40883 * However, there are a few limitations compared to array iteration: 40884 * 40885 * - The JavaScript specification does not define the order of keys 40886 * returned for an object, so Angular relies on the order returned by the browser 40887 * when running `for key in myObj`. Browsers generally follow the strategy of providing 40888 * keys in the order in which they were defined, although there are exceptions when keys are deleted 40889 * and reinstated. See the 40890 * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes). 40891 * 40892 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because 40893 * it's a prefix used by Angular for public (`$`) and private (`$$`) properties. 40894 * 40895 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with 40896 * objects, and will throw an error if used with one. 40897 * 40898 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array 40899 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could 40900 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter) 40901 * or implement a `$watch` on the object yourself. 40902 * 40903 * 40904 * # Tracking and Duplicates 40905 * 40906 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in 40907 * the collection. When a change happens, `ngRepeat` then makes the corresponding changes to the DOM: 40908 * 40909 * * When an item is added, a new instance of the template is added to the DOM. 40910 * * When an item is removed, its template instance is removed from the DOM. 40911 * * When items are reordered, their respective templates are reordered in the DOM. 40912 * 40913 * To minimize creation of DOM elements, `ngRepeat` uses a function 40914 * to "keep track" of all items in the collection and their corresponding DOM elements. 40915 * For example, if an item is added to the collection, `ngRepeat` will know that all other items 40916 * already have DOM elements, and will not re-render them. 40917 * 40918 * The default tracking function (which tracks items by their identity) does not allow 40919 * duplicate items in arrays. This is because when there are duplicates, it is not possible 40920 * to maintain a one-to-one mapping between collection items and DOM elements. 40921 * 40922 * If you do need to repeat duplicate items, you can substitute the default tracking behavior 40923 * with your own using the `track by` expression. 40924 * 40925 * For example, you may track items by the index of each item in the collection, using the 40926 * special scope property `$index`: 40927 * ```html 40928 * <div ng-repeat="n in [42, 42, 43, 43] track by $index"> 40929 * {{n}} 40930 * </div> 40931 * ``` 40932 * 40933 * You may also use arbitrary expressions in `track by`, including references to custom functions 40934 * on the scope: 40935 * ```html 40936 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)"> 40937 * {{n}} 40938 * </div> 40939 * ``` 40940 * 40941 * <div class="alert alert-success"> 40942 * If you are working with objects that have a unique identifier property, you should track 40943 * by this identifier instead of the object instance. Should you reload your data later, `ngRepeat` 40944 * will not have to rebuild the DOM elements for items it has already rendered, even if the 40945 * JavaScript objects in the collection have been substituted for new ones. For large collections, 40946 * this significantly improves rendering performance. If you don't have a unique identifier, 40947 * `track by $index` can also provide a performance boost. 40948 * </div> 40949 * 40950 * ```html 40951 * <div ng-repeat="model in collection track by model.id"> 40952 * {{model.name}} 40953 * </div> 40954 * ``` 40955 * 40956 * <br /> 40957 * <div class="alert alert-warning"> 40958 * Avoid using `track by $index` when the repeated template contains 40959 * {@link guide/expression#one-time-binding one-time bindings}. In such cases, the `nth` DOM 40960 * element will always be matched with the `nth` item of the array, so the bindings on that element 40961 * will not be updated even when the corresponding item changes, essentially causing the view to get 40962 * out-of-sync with the underlying data. 40963 * </div> 40964 * 40965 * When no `track by` expression is provided, it is equivalent to tracking by the built-in 40966 * `$id` function, which tracks items by their identity: 40967 * ```html 40968 * <div ng-repeat="obj in collection track by $id(obj)"> 40969 * {{obj.prop}} 40970 * </div> 40971 * ``` 40972 * 40973 * <br /> 40974 * <div class="alert alert-warning"> 40975 * **Note:** `track by` must always be the last expression: 40976 * </div> 40977 * ``` 40978 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id"> 40979 * {{model.name}} 40980 * </div> 40981 * ``` 40982 * 40983 * 40984 * # Special repeat start and end points 40985 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending 40986 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively. 40987 * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on) 40988 * up to and including the ending HTML tag where **ng-repeat-end** is placed. 40989 * 40990 * The example below makes use of this feature: 40991 * ```html 40992 * <header ng-repeat-start="item in items"> 40993 * Header {{ item }} 40994 * </header> 40995 * <div class="body"> 40996 * Body {{ item }} 40997 * </div> 40998 * <footer ng-repeat-end> 40999 * Footer {{ item }} 41000 * </footer> 41001 * ``` 41002 * 41003 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to: 41004 * ```html 41005 * <header> 41006 * Header A 41007 * </header> 41008 * <div class="body"> 41009 * Body A 41010 * </div> 41011 * <footer> 41012 * Footer A 41013 * </footer> 41014 * <header> 41015 * Header B 41016 * </header> 41017 * <div class="body"> 41018 * Body B 41019 * </div> 41020 * <footer> 41021 * Footer B 41022 * </footer> 41023 * ``` 41024 * 41025 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such 41026 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**). 41027 * 41028 * @animations 41029 * | Animation | Occurs | 41030 * |----------------------------------|-------------------------------------| 41031 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter | 41032 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out | 41033 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered | 41034 *
41035 * See the example below for defining CSS animations with ngRepeat. 41036 * 41037 * @element ANY 41038 * @scope 41039 * @priority 1000 41040 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These 41041 * formats are currently supported: 41042 * 41043 * * `variable in expression` – where variable is the user defined loop variable and `expression` 41044 * is a scope expression giving the collection to enumerate. 41045 * 41046 * For example: `album in artist.albums`. 41047 * 41048 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, 41049 * and `expression` is the scope expression giving the collection to enumerate. 41050 * 41051 * For example: `(name, age) in {'adam':10, 'amalie':12}`. 41052 * 41053 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression 41054 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression 41055 * is specified, ng-repeat associates elements by identity. It is an error to have 41056 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are 41057 * mapped to the same DOM element, which is not possible.) 41058 * 41059 * Note that the tracking expression must come last, after any filters, and the alias expression. 41060 * 41061 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements 41062 * will be associated by item identity in the array. 41063 * 41064 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique 41065 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements 41066 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM 41067 * element in the same way in the DOM. 41068 * 41069 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this 41070 * case the object identity does not matter. Two objects are considered equivalent as long as their `id` 41071 * property is same. 41072 * 41073 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter 41074 * to items in conjunction with a tracking expression. 41075 * 41076 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the 41077 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message 41078 * when a filter is active on the repeater, but the filtered result set is empty. 41079 * 41080 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after 41081 * the items have been processed through the filter. 41082 * 41083 * Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end 41084 * (and not as operator, inside an expression). 41085 * 41086 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` . 41087 * 41088 * @example 41089 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed 41090 * results by name or by age. New (entering) and removed (leaving) items are animated. 41091 <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true" name="ng-repeat"> 41092 <file name="index.html"> 41093 <div ng-controller="repeatController"> 41094 I have {{friends.length}} friends. They are: 41095 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" /> 41096 <ul class="example-animate-container"> 41097 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results"> 41098 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. 41099 </li> 41100 <li class="animate-repeat" ng-if="results.length === 0"> 41101 <strong>No results found...</strong> 41102 </li> 41103 </ul> 41104 </div> 41105 </file> 41106 <file name="script.js"> 41107 angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) { 41108 $scope.friends = [ 41109 {name:'John', age:25, gender:'boy'}, 41110 {name:'Jessie', age:30, gender:'girl'}, 41111 {name:'Johanna', age:28, gender:'girl'}, 41112 {name:'Joy', age:15, gender:'girl'}, 41113 {name:'Mary', age:28, gender:'girl'}, 41114 {name:'Peter', age:95, gender:'boy'}, 41115 {name:'Sebastian', age:50, gender:'boy'}, 41116 {name:'Erika', age:27, gender:'girl'}, 41117 {name:'Patrick', age:40, gender:'boy'}, 41118 {name:'Samantha', age:60, gender:'girl'} 41119 ]; 41120 }); 41121 </file> 41122 <file name="animations.css"> 41123 .example-animate-container { 41124 background:white; 41125 border:1px solid black; 41126 list-style:none; 41127 margin:0; 41128 padding:0 10px; 41129 } 41130 41131 .animate-repeat { 41132 line-height:30px; 41133 list-style:none; 41134 box-sizing:border-box; 41135 } 41136 41137 .animate-repeat.ng-move, 41138 .animate-repeat.ng-enter, 41139 .animate-repeat.ng-leave { 41140 transition:all linear 0.5s; 41141 } 41142 41143 .animate-repeat.ng-leave.ng-leave-active, 41144 .animate-repeat.ng-move, 41145 .animate-repeat.ng-enter { 41146 opacity:0; 41147 max-height:0; 41148 } 41149 41150 .animate-repeat.ng-leave,
41151 .animate-repeat.ng-move.ng-move-active, 41152 .animate-repeat.ng-enter.ng-enter-active { 41153 opacity:1; 41154 max-height:30px; 41155 } 41156 </file> 41157 <file name="protractor.js" type="protractor"> 41158 var friends = element.all(by.repeater('friend in friends')); 41159 41160 it('should render initial data set', function() { 41161 expect(friends.count()).toBe(10); 41162 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.'); 41163 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.'); 41164 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.'); 41165 expect(element(by.binding('friends.length')).getText()) 41166 .toMatch("I have 10 friends. They are:"); 41167 }); 41168 41169 it('should update repeater when filter predicate changes', function() { 41170 expect(friends.count()).toBe(10); 41171 41172 element(by.model('q')).sendKeys('ma'); 41173 41174 expect(friends.count()).toBe(2); 41175 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.'); 41176 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.'); 41177 }); 41178 </file> 41179 </example> 41180 */ 41181var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) { 41182 var NG_REMOVED = '$$NG_REMOVED'; 41183 var ngRepeatMinErr = minErr('ngRepeat'); 41184 41185 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) { 41186 // TODO(perf): generate setters to shave off ~40ms or 1-1.5% 41187 scope[valueIdentifier] = value; 41188 if (keyIdentifier) scope[keyIdentifier] = key; 41189 scope.$index = index; 41190 scope.$first = (index === 0); 41191 scope.$last = (index === (arrayLength - 1)); 41192 scope.$middle = !(scope.$first || scope.$last); 41193 // eslint-disable-next-line no-bitwise 41194 scope.$odd = !(scope.$even = (index & 1) === 0); 41195 }; 41196 41197 var getBlockStart = function(block) { 41198 return block.clone[0]; 41199 }; 41200 41201 var getBlockEnd = function(block) { 41202 return block.clone[block.clone.length - 1]; 41203 }; 41204 41205 41206 return { 41207 restrict: 'A', 41208 multiElement: true, 41209 transclude: 'element', 41210 priority: 1000, 41211 terminal: true, 41212 $$tlb: true, 41213 compile: function ngRepeatCompile($element, $attr) { 41214 var expression = $attr.ngRepeat; 41215 var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression); 41216 41217 var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); 41218 41219 if (!match) { 41220 throw ngRepeatMinErr('iexp', 'Expected expression in form of \'_item_ in _collection_[ track by _id_]\' but got \'{0}\'.', 41221 expression); 41222 } 41223 41224 var lhs = match[1]; 41225 var rhs = match[2]; 41226 var aliasAs = match[3]; 41227 var trackByExp = match[4]; 41228 41229 match = lhs.match(/^(?:(\s*[$\w]+)|\(\s*([$\w]+)\s*,\s*([$\w]+)\s*\))$/); 41230 41231 if (!match) { 41232 throw ngRepeatMinErr('iidexp', '\'_item_\' in \'_item_ in _collection_\' should be an identifier or \'(_key_, _value_)\' expression, but got \'{0}\'.', 41233 lhs); 41234 } 41235 var valueIdentifier = match[3] || match[1]; 41236 var keyIdentifier = match[2]; 41237 41238 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) || 41239 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) { 41240 throw ngRepeatMinErr('badident', 'alias \'{0}\' is invalid --- must be a valid JS identifier which is not a reserved name.', 41241 aliasAs); 41242 } 41243 41244 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn; 41245 var hashFnLocals = {$id: hashKey}; 41246 41247 if (trackByExp) { 41248 trackByExpGetter = $parse(trackByExp); 41249 } else { 41250 trackByIdArrayFn = function(key, value) { 41251 return hashKey(value); 41252 }; 41253 trackByIdObjFn = function(key) { 41254 return key; 41255 }; 41256 } 41257 41258 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) { 41259 41260 if (trackByExpGetter) { 41261 trackByIdExpFn = function(key, value, index) {
vendor: 14,350 bytes, lines 41262-41604
41262 // assign key, value, and $index to the locals so that they can be used in hash functions 41263 if (keyIdentifier) hashFnLocals[keyIdentifier] = key; 41264 hashFnLocals[valueIdentifier] = value; 41265 hashFnLocals.$index = index; 41266 return trackByExpGetter($scope, hashFnLocals); 41267 }; 41268 } 41269 41270 // Store a list of elements from previous run. This is a hash where key is the item from the 41271 // iterator, and the value is objects with following properties. 41272 // - scope: bound scope 41273 // - element: previous element. 41274 // - index: position 41275 // 41276 // We are using no-proto object so that we don't need to guard against inherited props via 41277 // hasOwnProperty. 41278 var lastBlockMap = createMap(); 41279 41280 //watch props 41281 $scope.$watchCollection(rhs, function ngRepeatAction(collection) { 41282 var index, length, 41283 previousNode = $element[0], // node that cloned nodes should be inserted after 41284 // initialized to the comment node anchor 41285 nextNode, 41286 // Same as lastBlockMap but it has the current state. It will become the 41287 // lastBlockMap on the next iteration. 41288 nextBlockMap = createMap(), 41289 collectionLength, 41290 key, value, // key/value of iteration 41291 trackById, 41292 trackByIdFn, 41293 collectionKeys, 41294 block, // last object information {scope, element, id} 41295 nextBlockOrder, 41296 elementsToRemove; 41297 41298 if (aliasAs) { 41299 $scope[aliasAs] = collection; 41300 } 41301 41302 if (isArrayLike(collection)) { 41303 collectionKeys = collection; 41304 trackByIdFn = trackByIdExpFn || trackByIdArrayFn; 41305 } else { 41306 trackByIdFn = trackByIdExpFn || trackByIdObjFn; 41307 // if object, extract keys, in enumeration order, unsorted 41308 collectionKeys = []; 41309 for (var itemKey in collection) { 41310 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') { 41311 collectionKeys.push(itemKey); 41312 } 41313 } 41314 } 41315 41316 collectionLength = collectionKeys.length; 41317 nextBlockOrder = new Array(collectionLength); 41318 41319 // locate existing items 41320 for (index = 0; index < collectionLength; index++) { 41321 key = (collection === collectionKeys) ? index : collectionKeys[index]; 41322 value = collection[key]; 41323 trackById = trackByIdFn(key, value, index); 41324 if (lastBlockMap[trackById]) { 41325 // found previously seen block 41326 block = lastBlockMap[trackById]; 41327 delete lastBlockMap[trackById]; 41328 nextBlockMap[trackById] = block; 41329 nextBlockOrder[index] = block; 41330 } else if (nextBlockMap[trackById]) { 41331 // if collision detected. restore lastBlockMap and throw an error 41332 forEach(nextBlockOrder, function(block) { 41333 if (block && block.scope) lastBlockMap[block.id] = block; 41334 }); 41335 throw ngRepeatMinErr('dupes', 41336 'Duplicates in a repeater are not allowed. Use \'track by\' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}', 41337 expression, trackById, value); 41338 } else { 41339 // new never before seen block 41340 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined}; 41341 nextBlockMap[trackById] = true; 41342 } 41343 } 41344 41345 // remove leftover items 41346 for (var blockKey in lastBlockMap) { 41347 block = lastBlockMap[blockKey]; 41348 elementsToRemove = getBlockNodes(block.clone); 41349 $animate.leave(elementsToRemove); 41350 if (elementsToRemove[0].parentNode) { 41351 // if the element was not removed yet because of pending animation, mark it as deleted 41352 // so that we can ignore it later 41353 for (index = 0, length = elementsToRemove.length; index < length; index++) { 41354 elementsToRemove[index][NG_REMOVED] = true; 41355 } 41356 } 41357 block.scope.$destroy(); 41358 } 41359 41360 // we are not using forEach for perf reasons (trying to avoid #call) 41361 for (index = 0; index < collectionLength; index++) { 41362 key = (collection === collectionKeys) ? index : collectionKeys[index]; 41363 value = collection[key]; 41364 block = nextBlockOrder[index]; 41365 41366 if (block.scope) { 41367 // if we have already seen this object, then we need to reuse the 41368 // associated scope/element 41369 41370 nextNode = previousNode; 41371 41372 // skip nodes that are already pending removal via leave animation 41373 do { 41374 nextNode = nextNode.nextSibling; 41375 } while (nextNode && nextNode[NG_REMOVED]); 41376 41377 if (getBlockStart(block) !== nextNode) { 41378 // existing item which got moved 41379 $animate.move(getBlockNodes(block.clone), null, previousNode); 41380 } 41381 previousNode = getBlockEnd(block); 41382 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); 41383 } else { 41384 // new item which we don't know about 41385 $transclude(function ngRepeatTransclude(clone, scope) { 41386 block.scope = scope; 41387 // http://jsperf.com/clone-vs-createcomment 41388 var endNode = ngRepeatEndComment.cloneNode(false); 41389 clone[clone.length++] = endNode; 41390 41391 $animate.enter(clone, null, previousNode); 41392 previousNode = endNode; 41393 // Note: We only need the first/last node of the cloned nodes. 41394 // However, we need to keep the reference to the jqlite wrapper as it might be changed later 41395 // by a directive with templateUrl when its template arrives. 41396 block.clone = clone; 41397 nextBlockMap[block.id] = block; 41398 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); 41399 }); 41400 } 41401 } 41402 lastBlockMap = nextBlockMap; 41403 }); 41404 }; 41405 } 41406 }; 41407}]; 41408 41409var NG_HIDE_CLASS = 'ng-hide'; 41410var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate'; 41411/** 41412 * @ngdoc directive 41413 * @name ngShow 41414 * @multiElement 41415 * 41416 * @description 41417 * The `ngShow` directive shows or hides the given HTML element based on the expression provided to 41418 * the `ngShow` attribute. 41419 * 41420 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element. 41421 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an 41422 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see 41423 * {@link ng.directive:ngCsp ngCsp}). 41424 * 41425 * ```html 41426 * <!-- when $scope.myValue is truthy (element is visible) --> 41427 * <div ng-show="myValue"></div> 41428 * 41429 * <!-- when $scope.myValue is falsy (element is hidden) --> 41430 * <div ng-show="myValue" class="ng-hide"></div> 41431 * ``` 41432 * 41433 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added 41434 * to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide` 41435 * CSS class is removed from the element causing the element not to appear hidden. 41436 * 41437 * ## Why is `!important` used? 41438 * 41439 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the 41440 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as 41441 * simple as changing the display style on a HTML list item would make hidden elements appear 41442 * visible. This also becomes a bigger issue when dealing with CSS frameworks. 41443 * 41444 * By using `!important`, the show and hide behavior will work as expected despite any clash between 41445 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a 41446 * developer chooses to override the styling to change how to hide an element then it is just a 41447 * matter of using `!important` in their own CSS code. 41448 * 41449 * ### Overriding `.ng-hide` 41450 * 41451 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you 41452 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for 41453 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually 41454 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added. 41455 * 41456 * ```css 41457 * .ng-hide:not(.ng-hide-animate) { 41458 * /* These are just alternative ways of hiding an element */ 41459 * display: block!important; 41460 * position: absolute; 41461 * top: -9999px; 41462 * left: -9999px; 41463 * } 41464 * ``` 41465 * 41466 * By default you don't need to override anything in CSS and the animations will work around the 41467 * display style. 41468 * 41469 * ## A note about animations with `ngShow` 41470 * 41471 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the 41472 * directive expression is true and false. This system works like the animation system present with 41473 * `ngClass` except that you must also include the `!important` flag to override the display 41474 * property so that the elements are not actually hidden during the animation. 41475 * 41476 * ```css 41477 * /* A working example can be found at the bottom of this page. */ 41478 * .my-element.ng-hide-add, .my-element.ng-hide-remove { 41479 * transition: all 0.5s linear; 41480 * } 41481 * 41482 * .my-element.ng-hide-add { ... } 41483 * .my-element.ng-hide-add.ng-hide-add-active { ... } 41484 * .my-element.ng-hide-remove { ... } 41485 * .my-element.ng-hide-remove.ng-hide-remove-active { ... } 41486 * ``` 41487 * 41488 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property 41489 * to block during animation states - ngAnimate will automatically handle the style toggling for you. 41490 * 41491 * @animations 41492 * | Animation | Occurs | 41493 * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------| 41494 * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. | 41495 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. | 41496 * 41497 * @element ANY 41498 * @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the 41499 * element is shown/hidden respectively. 41500 * 41501 * @example 41502 * A simple example, animating the element's opacity: 41503 * 41504 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple"> 41505 <file name="index.html"> 41506 Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br /> 41507 <div class="check-element animate-show-hide" ng-show="checked"> 41508 I show up when your checkbox is checked. 41509 </div> 41510 </file> 41511 <file name="animations.css"> 41512 .animate-show-hide.ng-hide { 41513 opacity: 0; 41514 } 41515 41516 .animate-show-hide.ng-hide-add, 41517 .animate-show-hide.ng-hide-remove { 41518 transition: all linear 0.5s; 41519 } 41520 41521 .check-element { 41522 border: 1px solid black; 41523 opacity: 1; 41524 padding: 10px; 41525 } 41526 </file> 41527 <file name="protractor.js" type="protractor"> 41528 it('should check ngShow', function() { 41529 var checkbox = element(by.model('checked')); 41530 var checkElem = element(by.css('.check-element')); 41531 41532 expect(checkElem.isDisplayed()).toBe(false); 41533 checkbox.click(); 41534 expect(checkElem.isDisplayed()).toBe(true); 41535 }); 41536 </file> 41537 </example> 41538 * 41539 * <hr /> 41540 * @example 41541 * A more complex example, featuring different show/hide animations: 41542 * 41543 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex"> 41544 <file name="index.html"> 41545 Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br /> 41546 <div class="check-element funky-show-hide" ng-show="checked"> 41547 I show up when your checkbox is checked. 41548 </div> 41549 </file> 41550 <file name="animations.css"> 41551 body { 41552 overflow: hidden; 41553 perspective: 1000px; 41554 } 41555 41556 .funky-show-hide.ng-hide-add { 41557 transform: rotateZ(0); 41558 transform-origin: right; 41559 transition: all 0.5s ease-in-out; 41560 } 41561 41562 .funky-show-hide.ng-hide-add.ng-hide-add-active { 41563 transform: rotateZ(-135deg); 41564 } 41565 41566 .funky-show-hide.ng-hide-remove { 41567 transform: rotateY(90deg); 41568 transform-origin: left; 41569 transition: all 0.5s ease; 41570 } 41571 41572 .funky-show-hide.ng-hide-remove.ng-hide-remove-active { 41573 transform: rotateY(0); 41574 } 41575 41576 .check-element { 41577 border: 1px solid black; 41578 opacity: 1; 41579 padding: 10px; 41580 } 41581 </file> 41582 <file name="protractor.js" type="protractor"> 41583 it('should check ngShow', function() { 41584 var checkbox = element(by.model('checked')); 41585 var checkElem = element(by.css('.check-element')); 41586 41587 expect(checkElem.isDisplayed()).toBe(false); 41588 checkbox.click(); 41589 expect(checkElem.isDisplayed()).toBe(true); 41590 }); 41591 </file> 41592 </example> 41593 */ 41594var ngShowDirective = ['$animate', function($animate) { 41595 return { 41596 restrict: 'A', 41597 multiElement: true, 41598 link: function(scope, element, attr) { 41599 scope.$watch(attr.ngShow, function ngShowWatchAction(value) { 41600 // we're adding a temporary, animation-specific class for ng-hide since this way 41601 // we can control when the element is actually displayed on screen without having 41602 // to have a global/greedy CSS selector that breaks when other animations are run. 41603 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845 41604 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
41605 tempClasses: NG_HIDE_IN_PROGRESS_CLASS 41606 }); 41607 }); 41608 } 41609 }; 41610}]; 41611 41612 41613/** 41614 * @ngdoc directive 41615 * @name ngHide 41616 * @multiElement 41617 * 41618 * @description 41619 * The `ngHide` directive shows or hides the given HTML element based on the expression provided to 41620 * the `ngHide` attribute. 41621 * 41622 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element. 41623 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an 41624 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see 41625 * {@link ng.directive:ngCsp ngCsp}). 41626 * 41627 * ```html 41628 * <!-- when $scope.myValue is truthy (element is hidden) --> 41629 * <div ng-hide="myValue" class="ng-hide"></div> 41630 * 41631 * <!-- when $scope.myValue is falsy (element is visible) --> 41632 * <div ng-hide="myValue"></div> 41633 * ``` 41634 * 41635 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added 41636 * to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide` 41637 * CSS class is removed from the element causing the element not to appear hidden. 41638 * 41639 * ## Why is `!important` used? 41640 * 41641 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the 41642 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as 41643 * simple as changing the display style on a HTML list item would make hidden elements appear 41644 * visible. This also becomes a bigger issue when dealing with CSS frameworks. 41645 * 41646 * By using `!important`, the show and hide behavior will work as expected despite any clash between 41647 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a 41648 * developer chooses to override the styling to change how to hide an element then it is just a 41649 * matter of using `!important` in their own CSS code. 41650 * 41651 * ### Overriding `.ng-hide` 41652 * 41653 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you 41654 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for 41655 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually 41656 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added. 41657 * 41658 * ```css 41659 * .ng-hide:not(.ng-hide-animate) { 41660 * /* These are just alternative ways of hiding an element */ 41661 * display: block!important; 41662 * position: absolute; 41663 * top: -9999px; 41664 * left: -9999px; 41665 * } 41666 * ``` 41667 * 41668 * By default you don't need to override in CSS anything and the animations will work around the 41669 * display style. 41670 * 41671 * ## A note about animations with `ngHide` 41672 * 41673 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the 41674 * directive expression is true and false. This system works like the animation system present with 41675 * `ngClass` except that you must also include the `!important` flag to override the display 41676 * property so that the elements are not actually hidden during the animation. 41677 * 41678 * ```css 41679 * /* A working example can be found at the bottom of this page. */ 41680 * .my-element.ng-hide-add, .my-element.ng-hide-remove { 41681 * transition: all 0.5s linear; 41682 * } 41683 * 41684 * .my-element.ng-hide-add { ... } 41685 * .my-element.ng-hide-add.ng-hide-add-active { ... } 41686 * .my-element.ng-hide-remove { ... } 41687 * .my-element.ng-hide-remove.ng-hide-remove-active { ... } 41688 * ``` 41689 * 41690 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property 41691 * to block during animation states - ngAnimate will automatically handle the style toggling for you. 41692 * 41693 * @animations 41694 * | Animation | Occurs | 41695 * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------| 41696 * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. | 41697 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. | 41698 * 41699 * 41700 * @element ANY 41701 * @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the 41702 * element is hidden/shown respectively. 41703 * 41704 * @example 41705 * A simple example, animating the element's opacity: 41706 * 41707 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple"> 41708 <file name="index.html"> 41709 Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br /> 41710 <div class="check-element animate-show-hide" ng-hide="checked"> 41711 I hide when your checkbox is checked. 41712 </div> 41713 </file> 41714 <file name="animations.css"> 41715 .animate-show-hide.ng-hide { 41716 opacity: 0; 41717 } 41718 41719 .animate-show-hide.ng-hide-add, 41720 .animate-show-hide.ng-hide-remove { 41721 transition: all linear 0.5s; 41722 } 41723 41724 .check-element { 41725 border: 1px solid black; 41726 opacity: 1; 41727 padding: 10px; 41728 } 41729 </file> 41730 <file name="protractor.js" type="protractor"> 41731 it('should check ngHide', function() { 41732 var checkbox = element(by.model('checked')); 41733 var checkElem = element(by.css('.check-element')); 41734 41735 expect(checkElem.isDisplayed()).toBe(true); 41736 checkbox.click(); 41737 expect(checkElem.isDisplayed()).toBe(false); 41738 }); 41739 </file> 41740 </example> 41741 * 41742 * <hr /> 41743 * @example 41744 * A more complex example, featuring different show/hide animations: 41745 * 41746 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex"> 41747 <file name="index.html"> 41748 Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br /> 41749 <div class="check-element funky-show-hide" ng-hide="checked"> 41750 I hide when your checkbox is checked. 41751 </div> 41752 </file> 41753 <file name="animations.css"> 41754 body { 41755 overflow: hidden; 41756 perspective: 1000px; 41757 } 41758 41759 .funky-show-hide.ng-hide-add { 41760 transform: rotateZ(0); 41761 transform-origin: right; 41762 transition: all 0.5s ease-in-out; 41763 } 41764 41765 .funky-show-hide.ng-hide-add.ng-hide-add-active { 41766 transform: rotateZ(-135deg); 41767 } 41768 41769 .funky-show-hide.ng-hide-remove { 41770 transform: rotateY(90deg); 41771 transform-origin: left; 41772 transition: all 0.5s ease; 41773 } 41774 41775 .funky-show-hide.ng-hide-remove.ng-hide-remove-active { 41776 transform: rotateY(0); 41777 } 41778 41779 .check-element { 41780 border: 1px solid black; 41781 opacity: 1; 41782 padding: 10px; 41783 } 41784 </file> 41785 <file name="protractor.js" type="protractor"> 41786 it('should check ngHide', function() { 41787 var checkbox = element(by.model('checked')); 41788 var checkElem = element(by.css('.check-element')); 41789 41790 expect(checkElem.isDisplayed()).toBe(true); 41791 checkbox.click(); 41792 expect(checkElem.isDisplayed()).toBe(false); 41793 }); 41794 </file> 41795 </example> 41796 */ 41797var ngHideDirective = ['$animate', function($animate) { 41798 return { 41799 restrict: 'A', 41800 multiElement: true, 41801 link: function(scope, element, attr) { 41802 scope.$watch(attr.ngHide, function ngHideWatchAction(value) { 41803 // The comment inside of the ngShowDirective explains why we add and 41804 // remove a temporary class for the show/hide animation 41805 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
41806 tempClasses: NG_HIDE_IN_PROGRESS_CLASS 41807 }); 41808 }); 41809 } 41810 }; 41811}]; 41812 41813/** 41814 * @ngdoc directive 41815 * @name ngStyle 41816 * @restrict AC 41817 * 41818 * @description 41819 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. 41820 * 41821 * @knownIssue 41822 * You should not use {@link guide/interpolation interpolation} in the value of the `style` 41823 * attribute, when using the `ngStyle` directive on the same element. 41824 * See {@link guide/interpolation#known-issues here} for more info. 41825 * 41826 * @element ANY 41827 * @param {expression} ngStyle 41828 * 41829 * {@link guide/expression Expression} which evals to an 41830 * object whose keys are CSS style names and values are corresponding values for those CSS 41831 * keys. 41832 * 41833 * Since some CSS style names are not valid keys for an object, they must be quoted. 41834 * See the 'background-color' style in the example below. 41835 * 41836 * @example 41837 <example name="ng-style"> 41838 <file name="index.html"> 41839 <input type="button" value="set color" ng-click="myStyle={color:'red'}"> 41840 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}"> 41841 <input type="button" value="clear" ng-click="myStyle={}"> 41842 <br/> 41843 <span ng-style="myStyle">Sample Text</span> 41844 <pre>myStyle={{myStyle}}</pre> 41845 </file> 41846 <file name="style.css"> 41847 span { 41848 color: black; 41849 } 41850 </file> 41851 <file name="protractor.js" type="protractor"> 41852 var colorSpan = element(by.css('span')); 41853 41854 it('should check ng-style', function() { 41855 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); 41856 element(by.css('input[value=\'set color\']')).click(); 41857 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)'); 41858 element(by.css('input[value=clear]')).click(); 41859 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); 41860 }); 41861 </file> 41862 </example> 41863 */ 41864var ngStyleDirective = ngDirective(function(scope, element, attr) { 41865 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { 41866 if (oldStyles && (newStyles !== oldStyles)) { 41867 forEach(oldStyles, function(val, style) { element.css(style, '');}); 41868 } 41869 if (newStyles) element.css(newStyles); 41870 }, true
vendor: 15,211 bytes, lines 41870-42259
41870); 41871}); 41872 41873/** 41874 * @ngdoc directive 41875 * @name ngSwitch 41876 * @restrict EA 41877 * 41878 * @description 41879 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression. 41880 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location 41881 * as specified in the template. 41882 * 41883 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it 41884 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element 41885 * matches the value obtained from the evaluated expression. In other words, you define a container element 41886 * (where you place the directive), place an expression on the **`on="..."` attribute** 41887 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place 41888 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on 41889 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default 41890 * attribute is displayed. 41891 * 41892 * <div class="alert alert-info"> 41893 * Be aware that the attribute values to match against cannot be expressions. They are interpreted 41894 * as literal string values to match against. 41895 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the 41896 * value of the expression `$scope.someVal`. 41897 * </div> 41898 41899 * @animations 41900 * | Animation | Occurs | 41901 * |----------------------------------|-------------------------------------| 41902 * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container | 41903 * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM | 41904 * 41905 * @usage 41906 * 41907 * ``` 41908 * <ANY ng-switch="expression"> 41909 * <ANY ng-switch-when="matchValue1">...</ANY> 41910 * <ANY ng-switch-when="matchValue2">...</ANY> 41911 * <ANY ng-switch-default>...</ANY> 41912 * </ANY> 41913 * ``` 41914 * 41915 * 41916 * @scope 41917 * @priority 1200 41918 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>. 41919 * On child elements add: 41920 * 41921 * * `ngSwitchWhen`: the case statement to match against. If match then this 41922 * case will be displayed. If the same match appears multiple times, all the 41923 * elements will be displayed. It is possible to associate multiple values to 41924 * the same `ngSwitchWhen` by defining the optional attribute 41925 * `ngSwitchWhenSeparator`. The separator will be used to split the value of 41926 * the `ngSwitchWhen` attribute into multiple tokens, and the element will show 41927 * if any of the `ngSwitch` evaluates to any of these tokens. 41928 * * `ngSwitchDefault`: the default case when no other case match. If there 41929 * are multiple default cases, all of them will be displayed when no other 41930 * case match. 41931 * 41932 * 41933 * @example 41934 <example module="switchExample" deps="angular-animate.js" animations="true" name="ng-switch"> 41935 <file name="index.html"> 41936 <div ng-controller="ExampleController"> 41937 <select ng-model="selection" ng-options="item for item in items"> 41938 </select> 41939 <code>selection={{selection}}</code> 41940 <hr/> 41941 <div class="animate-switch-container" 41942 ng-switch on="selection"> 41943 <div class="animate-switch" ng-switch-when="settings|options" ng-switch-when-separator="|">Settings Div</div> 41944 <div class="animate-switch" ng-switch-when="home">Home Span</div> 41945 <div class="animate-switch" ng-switch-default>default</div> 41946 </div> 41947 </div> 41948 </file> 41949 <file name="script.js"> 41950 angular.module('switchExample', ['ngAnimate']) 41951 .controller('ExampleController', ['$scope', function($scope) { 41952 $scope.items = ['settings', 'home', 'options', 'other']; 41953 $scope.selection = $scope.items[0]; 41954 }]); 41955 </file> 41956 <file name="animations.css"> 41957 .animate-switch-container { 41958 position:relative; 41959 background:white; 41960 border:1px solid black; 41961 height:40px; 41962 overflow:hidden; 41963 } 41964 41965 .animate-switch { 41966 padding:10px; 41967 } 41968 41969 .animate-switch.ng-animate { 41970 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 41971 41972 position:absolute; 41973 top:0; 41974 left:0; 41975 right:0; 41976 bottom:0; 41977 } 41978 41979 .animate-switch.ng-leave.ng-leave-active, 41980 .animate-switch.ng-enter { 41981 top:-50px; 41982 } 41983 .animate-switch.ng-leave, 41984 .animate-switch.ng-enter.ng-enter-active { 41985 top:0; 41986 } 41987 </file> 41988 <file name="protractor.js" type="protractor"> 41989 var switchElem = element(by.css('[ng-switch]')); 41990 var select = element(by.model('selection')); 41991 41992 it('should start in settings', function() { 41993 expect(switchElem.getText()).toMatch(/Settings Div/); 41994 }); 41995 it('should change to home', function() { 41996 select.all(by.css('option')).get(1).click(); 41997 expect(switchElem.getText()).toMatch(/Home Span/); 41998 }); 41999 it('should change to settings via "options"', function() { 42000 select.all(by.css('option')).get(2).click(); 42001 expect(switchElem.getText()).toMatch(/Settings Div/); 42002 }); 42003 it('should select default', function() { 42004 select.all(by.css('option')).get(3).click(); 42005 expect(switchElem.getText()).toMatch(/default/); 42006 }); 42007 </file> 42008 </example> 42009 */ 42010var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) { 42011 return { 42012 require: 'ngSwitch', 42013 42014 // asks for $scope to fool the BC controller module 42015 controller: ['$scope', function NgSwitchController() { 42016 this.cases = {}; 42017 }], 42018 link: function(scope, element, attr, ngSwitchController) { 42019 var watchExpr = attr.ngSwitch || attr.on, 42020 selectedTranscludes = [], 42021 selectedElements = [], 42022 previousLeaveAnimations = [], 42023 selectedScopes = []; 42024 42025 var spliceFactory = function(array, index) { 42026 return function(response) { 42027 if (response !== false) array.splice(index, 1); 42028 }; 42029 }; 42030 42031 scope.$watch(watchExpr, function ngSwitchWatchAction(value) { 42032 var i, ii; 42033 42034 // Start with the last, in case the array is modified during the loop 42035 while (previousLeaveAnimations.length) { 42036 $animate.cancel(previousLeaveAnimations.pop()); 42037 } 42038 42039 for (i = 0, ii = selectedScopes.length; i < ii; ++i) { 42040 var selected = getBlockNodes(selectedElements[i].clone); 42041 selectedScopes[i].$destroy(); 42042 var runner = previousLeaveAnimations[i] = $animate.leave(selected); 42043 runner.done(spliceFactory(previousLeaveAnimations, i)); 42044 } 42045 42046 selectedElements.length = 0; 42047 selectedScopes.length = 0; 42048 42049 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) { 42050 forEach(selectedTranscludes, function(selectedTransclude) { 42051 selectedTransclude.transclude(function(caseElement, selectedScope) { 42052 selectedScopes.push(selectedScope); 42053 var anchor = selectedTransclude.element; 42054 caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen'); 42055 var block = { clone: caseElement }; 42056 42057 selectedElements.push(block); 42058 $animate.enter(caseElement, anchor.parent(), anchor); 42059 }); 42060 }); 42061 } 42062 }); 42063 } 42064 }; 42065}]; 42066 42067var ngSwitchWhenDirective = ngDirective({ 42068 transclude: 'element', 42069 priority: 1200, 42070 require: '^ngSwitch', 42071 multiElement: true, 42072 link: function(scope, element, attrs, ctrl, $transclude) { 42073 42074 var cases = attrs.ngSwitchWhen.split(attrs.ngSwitchWhenSeparator).sort().filter( 42075 // Filter duplicate cases 42076 function(element, index, array) { return array[index - 1] !== element; } 42077 ); 42078 42079 forEach(cases, function(whenCase) { 42080 ctrl.cases['!' + whenCase] = (ctrl.cases['!' + whenCase] || []); 42081 ctrl.cases['!' + whenCase].push({ transclude: $transclude, element: element }); 42082 }); 42083 } 42084}); 42085 42086var ngSwitchDefaultDirective = ngDirective({ 42087 transclude: 'element', 42088 priority: 1200, 42089 require: '^ngSwitch', 42090 multiElement: true, 42091 link: function(scope, element, attr, ctrl, $transclude) { 42092 ctrl.cases['?'] = (ctrl.cases['?'] || []); 42093 ctrl.cases['?'].push({ transclude: $transclude, element: element }); 42094 } 42095}); 42096 42097/** 42098 * @ngdoc directive 42099 * @name ngTransclude 42100 * @restrict EAC 42101 * 42102 * @description 42103 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion. 42104 * 42105 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name 42106 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute. 42107 * 42108 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing 42109 * content of this element will be removed before the transcluded content is inserted. 42110 * If the transcluded content is empty (or only whitespace), the existing content is left intact. This lets you provide fallback 42111 * content in the case that no transcluded content is provided. 42112 * 42113 * @element ANY 42114 * 42115 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty 42116 * or its value is the same as the name of the attribute then the default slot is used. 42117 * 42118 * @example 42119 * ### Basic transclusion 42120 * This example demonstrates basic transclusion of content into a component directive. 42121 * <example name="simpleTranscludeExample" module="transcludeExample"> 42122 * <file name="index.html"> 42123 * <script> 42124 * angular.module('transcludeExample', []) 42125 * .directive('pane', function(){ 42126 * return { 42127 * restrict: 'E', 42128 * transclude: true, 42129 * scope: { title:'@' }, 42130 * template: '<div style="border: 1px solid black;">' + 42131 * '<div style="background-color: gray">{{title}}</div>' + 42132 * '<ng-transclude></ng-transclude>' + 42133 * '</div>' 42134 * }; 42135 * }) 42136 * .controller('ExampleController', ['$scope', function($scope) { 42137 * $scope.title = 'Lorem Ipsum'; 42138 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; 42139 * }]); 42140 * </script> 42141 * <div ng-controller="ExampleController"> 42142 * <input ng-model="title" aria-label="title"> <br/> 42143 * <textarea ng-model="text" aria-label="text"></textarea> <br/> 42144 * <pane title="{{title}}"><span>{{text}}</span></pane> 42145 * </div> 42146 * </file> 42147 * <file name="protractor.js" type="protractor"> 42148 * it('should have transcluded', function() { 42149 * var titleElement = element(by.model('title')); 42150 * titleElement.clear(); 42151 * titleElement.sendKeys('TITLE'); 42152 * var textElement = element(by.model('text')); 42153 * textElement.clear(); 42154 * textElement.sendKeys('TEXT'); 42155 * expect(element(by.binding('title')).getText()).toEqual('TITLE'); 42156 * expect(element(by.binding('text')).getText()).toEqual('TEXT'); 42157 * }); 42158 * </file> 42159 * </example> 42160 * 42161 * @example 42162 * ### Transclude fallback content 42163 * This example shows how to use `NgTransclude` with fallback content, that 42164 * is displayed if no transcluded content is provided. 42165 * 42166 * <example module="transcludeFallbackContentExample" name="ng-transclude"> 42167 * <file name="index.html"> 42168 * <script> 42169 * angular.module('transcludeFallbackContentExample', []) 42170 * .directive('myButton', function(){ 42171 * return { 42172 * restrict: 'E', 42173 * transclude: true, 42174 * scope: true, 42175 * template: '<button style="cursor: pointer;">' + 42176 * '<ng-transclude>' + 42177 * '<b style="color: red;">Button1</b>' + 42178 * '</ng-transclude>' + 42179 * '</button>' 42180 * }; 42181 * }); 42182 * </script> 42183 * <!-- fallback button content --> 42184 * <my-button id="fallback"></my-button> 42185 * <!-- modified button content --> 42186 * <my-button id="modified"> 42187 * <i style="color: green;">Button2</i> 42188 * </my-button> 42189 * </file> 42190 * <file name="protractor.js" type="protractor"> 42191 * it('should have different transclude element content', function() { 42192 * expect(element(by.id('fallback')).getText()).toBe('Button1'); 42193 * expect(element(by.id('modified')).getText()).toBe('Button2'); 42194 * }); 42195 * </file> 42196 * </example> 42197 * 42198 * @example 42199 * ### Multi-slot transclusion 42200 * This example demonstrates using multi-slot transclusion in a component directive. 42201 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample"> 42202 * <file name="index.html"> 42203 * <style> 42204 * .title, .footer { 42205 * background-color: gray 42206 * } 42207 * </style> 42208 * <div ng-controller="ExampleController"> 42209 * <input ng-model="title" aria-label="title"> <br/> 42210 * <textarea ng-model="text" aria-label="text"></textarea> <br/> 42211 * <pane> 42212 * <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title> 42213 * <pane-body><p>{{text}}</p></pane-body> 42214 * </pane> 42215 * </div> 42216 * </file> 42217 * <file name="app.js"> 42218 * angular.module('multiSlotTranscludeExample', []) 42219 * .directive('pane', function() { 42220 * return { 42221 * restrict: 'E', 42222 * transclude: { 42223 * 'title': '?paneTitle', 42224 * 'body': 'paneBody', 42225 * 'footer': '?paneFooter' 42226 * }, 42227 * template: '<div style="border: 1px solid black;">' + 42228 * '<div class="title" ng-transclude="title">Fallback Title</div>' + 42229 * '<div ng-transclude="body"></div>' + 42230 * '<div class="footer" ng-transclude="footer">Fallback Footer</div>' + 42231 * '</div>' 42232 * }; 42233 * }) 42234 * .controller('ExampleController', ['$scope', function($scope) { 42235 * $scope.title = 'Lorem Ipsum'; 42236 * $scope.link = 'https://google.com'; 42237 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; 42238 * }]); 42239 * </file> 42240 * <file name="protractor.js" type="protractor"> 42241 * it('should have transcluded the title and the body', function() { 42242 * var titleElement = element(by.model('title')); 42243 * titleElement.clear(); 42244 * titleElement.sendKeys('TITLE'); 42245 * var textElement = element(by.model('text')); 42246 * textElement.clear(); 42247 * textElement.sendKeys('TEXT'); 42248 * expect(element(by.css('.title')).getText()).toEqual('TITLE'); 42249 * expect(element(by.binding('text')).getText()).toEqual('TEXT'); 42250 * expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer'); 42251 * }); 42252 * </file> 42253 * </example> 42254 */ 42255var ngTranscludeMinErr = minErr('ngTransclude'); 42256var ngTranscludeDirective = ['$compile', function($compile) { 42257 return { 42258 restrict: 'EAC', 42259
42259terminal: true, 42260 compile: function ngTranscludeCompile(tElement) { 42261 42262 // Remove and cache any original content to act as a fallback 42263 var fallbackLinkFn = $compile(tElement.contents()); 42264 tElement.empty(); 42265 42266 return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) { 42267 42268 if (!$transclude) { 42269 throw ngTranscludeMinErr('orphan', 42270 'Illegal use of ngTransclude directive in the template! ' + 42271 'No parent directive that requires a transclusion found. ' + 42272 'Element: {0}', 42273 startingTag($element)); 42274 } 42275 42276 42277 // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default 42278 if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) { 42279 $attrs.ngTransclude = ''; 42280 } 42281 var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot; 42282 42283 // If the slot is required and no transclusion content is provided then this call will throw an error 42284 $transclude(ngTranscludeCloneAttachFn, null, slotName); 42285
vendor: 14,166 bytes, lines 42286-42691
42286 // If the slot is optional and no transclusion content is provided then use the fallback content 42287 if (slotName && !$transclude.isSlotFilled(slotName)) { 42288 useFallbackContent(); 42289 } 42290 42291 function ngTranscludeCloneAttachFn(clone, transcludedScope) { 42292 if (clone.length && notWhitespace(clone)) { 42293 $element.append(clone); 42294 } else { 42295 useFallbackContent(); 42296 // There is nothing linked against the transcluded scope since no content was available, 42297 // so it should be safe to clean up the generated scope. 42298 transcludedScope.$destroy(); 42299 } 42300 } 42301 42302 function useFallbackContent() { 42303 // Since this is the fallback content rather than the transcluded content, 42304 // we link against the scope of this directive rather than the transcluded scope 42305 fallbackLinkFn($scope, function(clone) { 42306 $element.append(clone); 42307 }); 42308 } 42309 42310 function notWhitespace(nodes) { 42311 for (var i = 0, ii = nodes.length; i < ii; i++) { 42312 var node = nodes[i]; 42313 if (node.nodeType !== NODE_TYPE_TEXT || node.nodeValue.trim()) { 42314 return true; 42315 } 42316 } 42317 } 42318 }; 42319 } 42320 }; 42321}]; 42322 42323/** 42324 * @ngdoc directive 42325 * @name script 42326 * @restrict E 42327 * 42328 * @description 42329 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the 42330 * template can be used by {@link ng.directive:ngInclude `ngInclude`}, 42331 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the 42332 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be 42333 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`. 42334 * 42335 * @param {string} type Must be set to `'text/ng-template'`. 42336 * @param {string} id Cache name of the template. 42337 * 42338 * @example 42339 <example name="script-tag"> 42340 <file name="index.html"> 42341 <script type="text/ng-template" id="/tpl.html"> 42342 Content of the template. 42343 </script> 42344 42345 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a> 42346 <div id="tpl-content" ng-include src="currentTpl"></div> 42347 </file> 42348 <file name="protractor.js" type="protractor"> 42349 it('should load template defined inside script tag', function() { 42350 element(by.css('#tpl-link')).click(); 42351 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/); 42352 }); 42353 </file> 42354 </example> 42355 */ 42356var scriptDirective = ['$templateCache', function($templateCache) { 42357 return { 42358 restrict: 'E', 42359 terminal: true, 42360 compile: function(element, attr) { 42361 if (attr.type === 'text/ng-template') { 42362 var templateUrl = attr.id, 42363 text = element[0].text; 42364 42365 $templateCache.put(templateUrl, text); 42366 } 42367 } 42368 }; 42369}]; 42370 42371/* exported selectDirective, optionDirective */ 42372 42373var noopNgModelController = { $setViewValue: noop, $render: noop }; 42374 42375function setOptionSelectedStatus(optionEl, value) { 42376 optionEl.prop('selected', value); // needed for IE 42377 /** 42378 * When unselecting an option, setting the property to null / false should be enough 42379 * However, screenreaders might react to the selected attribute instead, see 42380 * https://github.com/angular/angular.js/issues/14419 42381 * Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false 42382 * or null 42383 */ 42384 optionEl.attr('selected', value); 42385} 42386 42387/** 42388 * @ngdoc type 42389 * @name select.SelectController 42390 * @description 42391 * The controller for the `<select>` directive. This provides support for reading 42392 * and writing the selected value(s) of the control and also coordinates dynamically 42393 * added `<option>` elements, perhaps by an `ngRepeat` directive. 42394 */ 42395var SelectController = 42396 ['$element', '$scope', /** @this */ function($element, $scope) { 42397 42398 var self = this, 42399 optionsMap = new NgMap(); 42400 42401 self.selectValueMap = {}; // Keys are the hashed values, values the original values 42402 42403 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors 42404 self.ngModelCtrl = noopNgModelController; 42405 self.multiple = false; 42406 42407 // The "unknown" option is one that is prepended to the list if the viewValue 42408 // does not match any of the options. When it is rendered the value of the unknown 42409 // option is '? XXX ?' where XXX is the hashKey of the value that is not known. 42410 // 42411 // We can't just jqLite('<option>') since jqLite is not smart enough 42412 // to create it in <select> and IE barfs otherwise. 42413 self.unknownOption = jqLite(window.document.createElement('option')); 42414 42415 // The empty option is an option with the value '' that te application developer can 42416 // provide inside the select. When the model changes to a value that doesn't match an option, 42417 // it is selected - so if an empty option is provided, no unknown option is generated. 42418 // However, the empty option is not removed when the model matches an option. It is always selectable 42419 // and indicates that a "null" selection has been made. 42420 self.hasEmptyOption = false; 42421 self.emptyOption = undefined; 42422 42423 self.renderUnknownOption = function(val) { 42424 var unknownVal = self.generateUnknownOptionValue(val); 42425 self.unknownOption.val(unknownVal); 42426 $element.prepend(self.unknownOption); 42427 setOptionSelectedStatus(self.unknownOption, true); 42428 $element.val(unknownVal); 42429 }; 42430 42431 self.updateUnknownOption = function(val) { 42432 var unknownVal = self.generateUnknownOptionValue(val); 42433 self.unknownOption.val(unknownVal); 42434 setOptionSelectedStatus(self.unknownOption, true); 42435 $element.val(unknownVal); 42436 }; 42437 42438 self.generateUnknownOptionValue = function(val) { 42439 return '? ' + hashKey(val) + ' ?'; 42440 }; 42441 42442 self.removeUnknownOption = function() { 42443 if (self.unknownOption.parent()) self.unknownOption.remove(); 42444 }; 42445 42446 self.selectEmptyOption = function() { 42447 if (self.emptyOption) { 42448 $element.val(''); 42449 setOptionSelectedStatus(self.emptyOption, true); 42450 } 42451 }; 42452 42453 self.unselectEmptyOption = function() { 42454 if (self.hasEmptyOption) { 42455 self.emptyOption.removeAttr('selected'); 42456 } 42457 }; 42458 42459 $scope.$on('$destroy', function() { 42460 // disable unknown option so that we don't do work when the whole select is being destroyed 42461 self.renderUnknownOption = noop; 42462 }); 42463 42464 // Read the value of the select control, the implementation of this changes depending 42465 // upon whether the select can have multiple values and whether ngOptions is at work. 42466 self.readValue = function readSingleValue() { 42467 var val = $element.val(); 42468 // ngValue added option values are stored in the selectValueMap, normal interpolations are not 42469 var realVal = val in self.selectValueMap ? self.selectValueMap[val] : val; 42470 42471 if (self.hasOption(realVal)) { 42472 return realVal; 42473 } 42474 42475 return null; 42476 }; 42477 42478 42479 // Write the value to the select control, the implementation of this changes depending 42480 // upon whether the select can have multiple values and whether ngOptions is at work. 42481 self.writeValue = function writeSingleValue(value) { 42482 // Make sure to remove the selected attribute from the previously selected option 42483 // Otherwise, screen readers might get confused 42484 var currentlySelectedOption = $element[0].options[$element[0].selectedIndex]; 42485 if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false); 42486 42487 if (self.hasOption(value)) { 42488 self.removeUnknownOption(); 42489 42490 var hashedVal = hashKey(value); 42491 $element.val(hashedVal in self.selectValueMap ? hashedVal : value); 42492 42493 // Set selected attribute and property on selected option for screen readers 42494 var selectedOption = $element[0].options[$element[0].selectedIndex]; 42495 setOptionSelectedStatus(jqLite(selectedOption), true); 42496 } else { 42497 if (value == null && self.emptyOption) { 42498 self.removeUnknownOption(); 42499 self.selectEmptyOption(); 42500 } else if (self.unknownOption.parent().length) { 42501 self.updateUnknownOption(value); 42502 } else { 42503 self.renderUnknownOption(value); 42504 } 42505 } 42506 }; 42507 42508 42509 // Tell the select control that an option, with the given value, has been added 42510 self.addOption = function(value, element) { 42511 // Skip comment nodes, as they only pollute the `optionsMap` 42512 if (element[0].nodeType === NODE_TYPE_COMMENT) return; 42513 42514 assertNotHasOwnProperty(value, '"option value"'); 42515 if (value === '') { 42516 self.hasEmptyOption = true; 42517 self.emptyOption = element; 42518 } 42519 var count = optionsMap.get(value) || 0; 42520 optionsMap.set(value, count + 1); 42521 // Only render at the end of a digest. This improves render performance when many options 42522 // are added during a digest and ensures all relevant options are correctly marked as selected 42523 scheduleRender(); 42524 }; 42525 42526 // Tell the select control that an option, with the given value, has been removed 42527 self.removeOption = function(value) { 42528 var count = optionsMap.get(value); 42529 if (count) { 42530 if (count === 1) { 42531 optionsMap.delete(value); 42532 if (value === '') { 42533 self.hasEmptyOption = false; 42534 self.emptyOption = undefined; 42535 } 42536 } else { 42537 optionsMap.set(value, count - 1); 42538 } 42539 } 42540 }; 42541 42542 // Check whether the select control has an option matching the given value 42543 self.hasOption = function(value) { 42544 return !!optionsMap.get(value); 42545 }; 42546 42547 42548 var renderScheduled = false; 42549 function scheduleRender() { 42550 if (renderScheduled) return; 42551 renderScheduled = true; 42552 $scope.$$postDigest(function() { 42553 renderScheduled = false; 42554 self.ngModelCtrl.$render(); 42555 }); 42556 } 42557 42558 var updateScheduled = false; 42559 function scheduleViewValueUpdate(renderAfter) { 42560 if (updateScheduled) return; 42561 42562 updateScheduled = true; 42563 42564 $scope.$$postDigest(function() { 42565 if ($scope.$$destroyed) return; 42566 42567 updateScheduled = false; 42568 self.ngModelCtrl.$setViewValue(self.readValue()); 42569 if (renderAfter) self.ngModelCtrl.$render(); 42570 }); 42571 } 42572 42573 42574 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) { 42575 42576 if (optionAttrs.$attr.ngValue) { 42577 // The value attribute is set by ngValue 42578 var oldVal, hashedVal = NaN; 42579 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) { 42580 42581 var removal; 42582 var previouslySelected = optionElement.prop('selected'); 42583 42584 if (isDefined(hashedVal)) { 42585 self.removeOption(oldVal); 42586 delete self.selectValueMap[hashedVal]; 42587 removal = true; 42588 } 42589 42590 hashedVal = hashKey(newVal); 42591 oldVal = newVal; 42592 self.selectValueMap[hashedVal] = newVal; 42593 self.addOption(newVal, optionElement); 42594 // Set the attribute directly instead of using optionAttrs.$set - this stops the observer 42595 // from firing a second time. Other $observers on value will also get the result of the 42596 // ngValue expression, not the hashed value 42597 optionElement.attr('value', hashedVal); 42598 42599 if (removal && previouslySelected) { 42600 scheduleViewValueUpdate(); 42601 } 42602 42603 }); 42604 } else if (interpolateValueFn) { 42605 // The value attribute is interpolated 42606 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) { 42607 // This method is overwritten in ngOptions and has side-effects! 42608 self.readValue(); 42609 42610 var removal; 42611 var previouslySelected = optionElement.prop('selected'); 42612 42613 if (isDefined(oldVal)) { 42614 self.removeOption(oldVal); 42615 removal = true; 42616 } 42617 oldVal = newVal; 42618 self.addOption(newVal, optionElement); 42619 42620 if (removal && previouslySelected) { 42621 scheduleViewValueUpdate(); 42622 } 42623 }); 42624 } else if (interpolateTextFn) { 42625 // The text content is interpolated 42626 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) { 42627 optionAttrs.$set('value', newVal); 42628 var previouslySelected = optionElement.prop('selected'); 42629 if (oldVal !== newVal) { 42630 self.removeOption(oldVal); 42631 } 42632 self.addOption(newVal, optionElement); 42633 42634 if (oldVal && previouslySelected) { 42635 scheduleViewValueUpdate(); 42636 } 42637 }); 42638 } else { 42639 // The value attribute is static 42640 self.addOption(optionAttrs.value, optionElement); 42641 } 42642 42643 42644 optionAttrs.$observe('disabled', function(newVal) { 42645 42646 // Since model updates will also select disabled options (like ngOptions), 42647 // we only have to handle options becoming disabled, not enabled 42648 42649 if (newVal === 'true' || newVal && optionElement.prop('selected')) { 42650 if (self.multiple) { 42651 scheduleViewValueUpdate(true); 42652 } else { 42653 self.ngModelCtrl.$setViewValue(null); 42654 self.ngModelCtrl.$render(); 42655 } 42656 } 42657 }); 42658 42659 optionElement.on('$destroy', function() { 42660 var currentValue = self.readValue(); 42661 var removeValue = optionAttrs.value; 42662 42663 self.removeOption(removeValue); 42664 scheduleRender(); 42665 42666 if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 || 42667 currentValue === removeValue 42668 ) { 42669 // When multiple (selected) options are destroyed at the same time, we don't want 42670 // to run a model update for each of them. Instead, run a single update in the $$postDigest 42671 scheduleViewValueUpdate(true); 42672 } 42673 }); 42674 }; 42675}]; 42676 42677/** 42678 * @ngdoc directive 42679 * @name select 42680 * @restrict E 42681 * 42682 * @description 42683 * HTML `select` element with angular data-binding. 42684 * 42685 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding 42686 * between the scope and the `<select>` control (including setting default values). 42687 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or 42688 * {@link ngOptions `ngOptions`} directives. 42689 * 42690 * When an item in the `<select>` menu is selected, the value of the selected option will be bound 42691 * to the model identified by the `ngModel` directive. With static or repeated o
42691ptions, this is 42692 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing. 42693 * Value and textContent can be interpolated. 42694 * 42695 * ## Matching model and option values 42696 * 42697 * In general, the match between the model and an option is evaluated by strictly comparing the model 42698 * value against the value of the available options. 42699 * 42700 * If you are setting the option value with the option's `value` attribute, or textContent, the 42701 * value will always be a `string` which means that the model value must also be a string. 42702 * Otherwise the `select` directive cannot match them correctly. 42703 * 42704 * To bind the model to a non-string value, you can use one of the following strategies: 42705 * - the {@link ng.ngOptions `ngOptions`} directive 42706 * ({@link ng.select#using-select-with-ngoptions-and-setting-a-default-value}) 42707 * - the {@link ng.ngValue `ngValue`} directive, which allows arbitrary expressions to be 42708 * option values ({@link ng.select#using-ngvalue-to-bind-the-model-to-an-array-of-objects Example}) 42709 * - model $parsers / $formatters to convert the string value 42710 * ({@link ng.select#binding-select-to-a-non-string-value-via-ngmodel-parsing-formatting Example}) 42711 * 42712 * If the viewValue of `ngModel` does not match any of the options, then the control 42713 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved. 42714 * 42715 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can 42716 * be nested into the `<select>` element. This element will then represent the `null` or "not selected" 42717 * option. See example below for demonstration. 42718 * 42719 * ## Choosing between `ngRepeat` and `ngOptions` 42720 * 42721 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions 42722 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits: 42723 * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the 42724 * comprehension expression 42725 * - reduced memory consumption by not creating a new scope for each repeated instance 42726 * - increased render speed by creating the options in a documentFragment instead of individually 42727 * 42728 * Specifically, select with repeated options slows down significantly starting at 2000 options in 42729 * Chrome and Internet Explorer / Edge. 42730 * 42731 * 42732 * @param {string} ngModel Assignable angular expression to data-bind to. 42733 * @param {string=} name Property name of the form under which the control is published. 42734 * @param {string=} multiple Allows multiple options to be selected. The selected values will be 42735 * bound to the model as an array. 42736 * @param {string=} required Sets `required` validation error key if the value is not entered. 42737 * @param {string=} ngRequired Adds required attribute and required validation constraint to 42738 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required 42739 * when you want to data-bind to the required attribute. 42740 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user 42741 * interaction with the select element. 42742 * @param {string=} ngOptions sets the options that the select is populated with and defines what is 42743 * set on the model on selection. See {@link ngOptions `ngOptions`}. 42744 * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the 42745 * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive. 42746 * 42747 * @example 42748 * ### Simple `select` elements with static options 42749 * 42750 * <example name="static-select" module="staticSelect"> 42751 * <file name="index.html">
42752 * <div ng-controller="ExampleController"> 42753 * <form name="myForm"> 42754 * <label for="singleSelect"> Single select: </label><br> 42755 * <select name="singleSelect" ng-model="data.singleSelect"> 42756 * <option value="option-1">Option 1</option> 42757 * <option value="option-2">Option 2</option> 42758 * </select><br> 42759 * 42760 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br> 42761 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect"> 42762 * <option value="">---Please select---</option> <!-- not selected / blank option --> 42763 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation --> 42764 * <option value="option-2">Option 2</option> 42765 * </select><br> 42766 * <button ng-click="forceUnknownOption()">Force unknown option</button><br> 42767 * <tt>singleSelect = {{data.singleSelect}}</tt> 42768 * 42769 * <hr> 42770 * <label for="multipleSelect"> Multiple select: </label><br> 42771 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple> 42772 * <option value="option-1">Option 1</option> 42773 * <option value="option-2">Option 2</option> 42774 * <option value="option-3">Option 3</option> 42775 * </select><br> 42776 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/> 42777 * </form> 42778 * </div> 42779 * </file> 42780 * <file name="app.js"> 42781 * angular.module('staticSelect', []) 42782 * .controller('ExampleController', ['$scope', function($scope) { 42783 * $scope.data = { 42784 * singleSelect: null, 42785 * multipleSelect: [], 42786 * option1: 'option-1' 42787 * }; 42788 * 42789 * $scope.forceUnknownOption = function() { 42790 * $scope.data.singleSelect = 'nonsense'; 42791 * }; 42792 * }]); 42793 * </file> 42794 *</example> 42795 * 42796 * ### Using `ngRepeat` to generate `select` options 42797 * <example name="select-ngrepeat" module="ngrepeatSelect"> 42798 * <file name="index.html">
42799 * <div ng-controller="ExampleController"> 42800 * <form name="myForm"> 42801 * <label for="repeatSelect"> Repeat select: </label> 42802 * <select name="repeatSelect" id="repeatSelect" ng-model="data.model"> 42803 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option> 42804 * </select> 42805 * </form> 42806 * <hr> 42807 * <tt>model = {{data.model}}</tt><br/> 42808 * </div> 42809 * </file> 42810 * <file name="app.js"> 42811 * angular.module('ngrepeatSelect', []) 42812 * .controller('ExampleController', ['$scope', function($scope) { 42813 * $scope.data = { 42814 * model: null, 42815 * availableOptions: [ 42816 * {id: '1', name: 'Option A'}, 42817 * {id: '2', name: 'Option B'}, 42818 * {id: '3', name: 'Option C'} 42819 * ] 42820 * }; 42821 * }]); 42822 * </file> 42823 *</example> 42824 * 42825 * ### Using `ngValue` to bind the model to an array of objects 42826 * <example name="select-ngvalue" module="ngvalueSelect"> 42827 * <file name="index.html"> 42828 * <div ng-controller="ExampleController"> 42829 * <form name="myForm"> 42830 * <label for="ngvalueselect"> ngvalue select: </label> 42831 * <select size="6" name="ngvalueselect" ng-model="data.model" multiple> 42832 * <option ng-repeat="option in data.availableOptions" ng-value="option.value">{{option.name}}</option> 42833 * </select> 42834 * </form> 42835 * <hr> 42836 * <pre>model = {{data.model | json}}</pre><br/> 42837 * </div> 42838 * </file> 42839 * <file name="app.js"> 42840 * angular.module('ngvalueSelect', []) 42841 * .controller('ExampleController', ['$scope', function($scope) { 42842 * $scope.data = { 42843 * model: null, 42844 * availableOptions: [ 42845 {value: 'myString', name: 'string'}, 42846 {value: 1, name: 'integer'}, 42847 {value: true, name: 'boolean'}, 42848 {value: null, name: 'null'}, 42849 {value: {prop: 'value'}, name: 'object'}, 42850 {value: ['a'], name: 'array'} 42851 * ] 42852 * }; 42853 * }]); 42854 * </file> 42855 *</example> 42856 * 42857 * ### Using `select` with `ngOptions` and setting a default value 42858 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples. 42859 * 42860 * <example name="select-with-default-values" module="defaultValueSelect"> 42861 * <file name="index.html">
42862 * <div ng-controller="ExampleController"> 42863 * <form name="myForm"> 42864 * <label for="mySelect">Make a choice:</label> 42865 * <select name="mySelect" id="mySelect" 42866 * ng-options="option.name for option in data.availableOptions track by option.id" 42867 * ng-model="data.selectedOption"></select> 42868 * </form> 42869 * <hr> 42870 * <tt>option = {{data.selectedOption}}</tt><br/> 42871 * </div> 42872 * </file> 42873 * <file name="app.js"> 42874 * angular.module('defaultValueSelect', []) 42875 * .controller('ExampleController', ['$scope', function($scope) { 42876 * $scope.data = { 42877 * availableOptions: [ 42878 * {id: '1', name: 'Option A'}, 42879 * {id: '2', name: 'Option B'}, 42880 * {id: '3', name: 'Option C'} 42881 * ], 42882 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui 42883 * }; 42884 * }]); 42885 * </file> 42886 *</example> 42887 * 42888 * 42889 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting 42890 * 42891 * <example name="select-with-non-string-options" module="nonStringSelect"> 42892 * <file name="index.html"> 42893 * <select ng-model="model.id" convert-to-number> 42894 * <option value="0">Zero</option> 42895 * <option value="1">One</option> 42896 * <option value="2">Two</option> 42897 * </select> 42898 * {{ model }} 42899 * </file> 42900 * <file name="app.js"> 42901 * angular.module('nonStringSelect', []) 42902 * .run(function($rootScope) { 42903 * $rootScope.model = { id: 2 }; 42904 * }) 42905 * .directive('convertToNumber', function() { 42906 * return { 42907 * require: 'ngModel', 42908 * link: function(scope, element, attrs, ngModel) { 42909 * ngModel.$parsers.push(function(val) { 42910 * return parseInt(val, 10); 42911 * }); 42912 * ngModel.$formatters.push(function(val) { 42913 * return '' + val; 42914 * }); 42915 * } 42916 * }; 42917 * }); 42918 * </file> 42919 * <file name="protractor.js" type="protractor"> 42920 * it('should initialize to model', function() { 42921 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two'); 42922 * }); 42923 * </file> 42924 * </example> 42925 * 42926 */ 42927var selectDirective = function() { 42928 42929 return { 42930 restrict: 'E', 42931 require: ['select', '?ngModel'], 42932 controller: SelectController, 42933 priority: 1, 42934 link: { 42935 pre: selectPreLink, 42936 post: selectPostLink 42937 } 42938 }; 42939 42940 function selectPreLink(scope, element, attr, ctrls) { 42941 42942 var selectCtrl = ctrls[0]; 42943 var ngModelCtrl = ctrls[1]; 42944 42945 // if ngModel is not defined, we don't need to do anything but set the registerOption 42946 // function to noop, so options don't get added internally 42947 if (!ngModelCtrl) { 42948 selectCtrl.registerOption = noop; 42949 return; 42950 } 42951 42952 42953 selectCtrl.ngModelCtrl = ngModelCtrl; 42954 42955 // When the selected item(s) changes we delegate getting the value of the select control 42956 // to the `readValue` method, which can be changed if the select can have multiple 42957 // selected values or if the options are being generated by `ngOptions` 42958 element.on('change', function() { 42959 selectCtrl.removeUnknownOption(); 42960 scope.$apply(function() { 42961 ngModelCtrl.$setViewValue(selectCtrl.readValue()); 42962 }); 42963 }); 42964 42965 // If the select allows multiple values then we need to modify how we read and write 42966 // values from and to the control; also what it means for the value to be empty and 42967 // we have to add an extra watch since ngModel doesn't work well with arrays - it 42968 // doesn't trigger rendering if only an item in the array changes. 42969 if (attr.multiple) { 42970 selectCtrl.multiple = true; 42971 42972 // Read value now needs to check each option to see if it is selected 42973 selectCtrl.readValue = function readMultipleValue() { 42974 var array = []; 42975 forEach(element.find('option'), function(option) { 42976 if (option.selected && !option.disabled) { 42977 var val = option.value; 42978 array.push(val in selectCtrl.selectValueMap ? selectCtrl.selectValueMap[val] : val); 42979 } 42980 }); 42981 return array; 42982 }; 42983 42984 // Write value now needs to set the selected property of each matching option 42985 selectCtrl.writeValue = function writeMultipleValue(value) {
vendor: 5,037 bytes, lines 42986-43102
42986 forEach(element.find('option'), function(option) { 42987 var shouldBeSelected = !!value && (includes(value, option.value) || 42988 includes(value, selectCtrl.selectValueMap[option.value])); 42989 var currentlySelected = option.selected; 42990 42991 // IE and Edge, adding options to the selection via shift+click/UP/DOWN, 42992 // will de-select already selected options if "selected" on those options was set 42993 // more than once (i.e. when the options were already selected) 42994 // So we only modify the selected property if neccessary. 42995 // Note: this behavior cannot be replicated via unit tests because it only shows in the 42996 // actual user interface. 42997 if (shouldBeSelected !== currentlySelected) { 42998 setOptionSelectedStatus(jqLite(option), shouldBeSelected); 42999 } 43000 43001 }); 43002 }; 43003 43004 // we have to do it on each watch since ngModel watches reference, but 43005 // we need to work of an array, so we need to see if anything was inserted/removed 43006 var lastView, lastViewRef = NaN; 43007 scope.$watch(function selectMultipleWatch() { 43008 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) { 43009 lastView = shallowCopy(ngModelCtrl.$viewValue); 43010 ngModelCtrl.$render(); 43011 } 43012 lastViewRef = ngModelCtrl.$viewValue; 43013 }); 43014 43015 // If we are a multiple select then value is now a collection 43016 // so the meaning of $isEmpty changes 43017 ngModelCtrl.$isEmpty = function(value) { 43018 return !value || value.length === 0; 43019 }; 43020 43021 } 43022 } 43023 43024 function selectPostLink(scope, element, attrs, ctrls) { 43025 // if ngModel is not defined, we don't need to do anything 43026 var ngModelCtrl = ctrls[1]; 43027 if (!ngModelCtrl) return; 43028 43029 var selectCtrl = ctrls[0]; 43030 43031 // We delegate rendering to the `writeValue` method, which can be changed 43032 // if the select can have multiple selected values or if the options are being 43033 // generated by `ngOptions`. 43034 // This must be done in the postLink fn to prevent $render to be called before 43035 // all nodes have been linked correctly. 43036 ngModelCtrl.$render = function() { 43037 selectCtrl.writeValue(ngModelCtrl.$viewValue); 43038 }; 43039 } 43040}; 43041 43042 43043// The option directive is purely designed to communicate the existence (or lack of) 43044// of dynamically created (and destroyed) option elements to their containing select 43045// directive via its controller. 43046var optionDirective = ['$interpolate', function($interpolate) { 43047 return { 43048 restrict: 'E', 43049 priority: 100, 43050 compile: function(element, attr) { 43051 var interpolateValueFn, interpolateTextFn; 43052 43053 if (isDefined(attr.ngValue)) { 43054 // Will be handled by registerOption 43055 } else if (isDefined(attr.value)) { 43056 // If the value attribute is defined, check if it contains an interpolation 43057 interpolateValueFn = $interpolate(attr.value, true); 43058 } else { 43059 // If the value attribute is not defined then we fall back to the 43060 // text content of the option element, which may be interpolated 43061 interpolateTextFn = $interpolate(element.text(), true); 43062 if (!interpolateTextFn) { 43063 attr.$set('value', element.text()); 43064 } 43065 } 43066 43067 return function(scope, element, attr) { 43068 // This is an optimization over using ^^ since we don't want to have to search 43069 // all the way to the root of the DOM for every single option element 43070 var selectCtrlName = '$selectController', 43071 parent = element.parent(), 43072 selectCtrl = parent.data(selectCtrlName) || 43073 parent.parent().data(selectCtrlName); // in case we are in optgroup 43074 43075 if (selectCtrl) { 43076 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn); 43077 } 43078 }; 43079 } 43080 }; 43081}]; 43082 43083/** 43084 * @ngdoc directive 43085 * @name ngRequired 43086 * @restrict A 43087 * 43088 * @description 43089 * 43090 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. 43091 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be 43092 * applied to custom controls. 43093 * 43094 * The directive sets the `required` attribute on the element if the Angular expression inside 43095 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we 43096 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide} 43097 * for more info. 43098 * 43099 * The validator will set the `required` error key to true if the `required` attribute is set and 43100 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the 43101 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the 43102 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
43103 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based. 43104 * 43105 * @example 43106 * <example name="ngRequiredDirective" module="ngRequiredExample"> 43107 * <file name="index.html"> 43108 * <script> 43109 * angular.module('ngRequiredExample', []) 43110 * .controller('ExampleController', ['$scope', function($scope) { 43111 * $scope.required = true; 43112 * }]); 43113 * </script> 43114 * <div ng-controller="ExampleController"> 43115 * <form name="form"> 43116 * <label for="required">Toggle required: </label> 43117 * <input type="checkbox" ng-model="required" id="required" /> 43118 * <br> 43119 * <label for="input">This input must be filled if `required` is true: </label> 43120 * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br> 43121 * <hr> 43122 * required error set? = <code>{{form.input.$error.required}}</code><br> 43123 * model = <code>{{model}}</code> 43124 * </form> 43125 * </div> 43126 * </file> 43127 * <file name="protractor.js" type="protractor"> 43128 var required = element(by.binding('form.input.$error.required')); 43129 var model = element(by.binding('model')); 43130 var input = element(by.id('input')); 43131 43132 it('should set the required error', function() { 43133 expect(required.getText()).toContain('true'); 43134 43135 input.sendKeys('123'); 43136 expect(required.getText()).not.toContain('true'); 43137 expect(model.getText()).toContain('123'); 43138 }); 43139 * </file> 43140 * </example> 43141 */ 43142var requiredDirective = function() { 43143 return { 43144 restrict: 'A', 43145 require: '?ngModel', 43146 link: function(scope, elm, attr, ctrl) { 43147 if (!ctrl) return; 43148 attr.required = true; // force truthy in case we are on non input element 43149 43150 ctrl.$validators.required = function(modelValue, viewValue) { 43151 return !attr.required || !ctrl.$isEmpty(viewValue); 43152 }; 43153 43154 attr.$observe('required', function() { 43155 ctrl.$validate(); 43156 }); 43157 } 43158 }; 43159}; 43160 43161/** 43162 * @ngdoc directive 43163 * @name ngPattern 43164 * 43165 * @description 43166 * 43167 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. 43168 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls. 43169 * 43170 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} 43171 * does not match a RegExp which is obtained by evaluating the Angular expression given in the 43172 * `ngPattern` attribute value: 43173 * * If the expression evaluates to a RegExp object, then this is used directly. 43174 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it 43175 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. 43176 * 43177 * <div class="alert alert-info"> 43178 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to 43179 * start at the index of the last search's match, thus not taking the whole input value into 43180 * account. 43181 * </div> 43182 * 43183 * <div class="alert alert-info"> 43184 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two 43185 * differences: 43186 * <ol> 43187 * <li> 43188 * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is 43189 * not available. 43190 * </li> 43191 * <li> 43192 * The `ngPattern` attribute must be an expression, while the `pattern` value must be 43193 * interpolated. 43194 * </li> 43195 * </ol> 43196 * </div> 43197 * 43198 * @example 43199 * <example name="ngPatternDirective" module="ngPatternExample"> 43200 * <file name="index.html"> 43201 * <script> 43202 * angular.module('ngPatternExample', []) 43203 * .controller('ExampleController', ['$scope', function($scope) { 43204 * $scope.regex = '\\d+'; 43205 * }]); 43206 * </script>
43207 * <div ng-controller="ExampleController"> 43208 * <form name="form"> 43209 * <label for="regex">Set a pattern (regex string): </label> 43210 * <input type="text" ng-model="regex" id="regex" /> 43211 * <br> 43212 * <label for="input">This input is restricted by the current pattern: </label> 43213 * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br> 43214 * <hr> 43215 * input valid? = <code>{{form.input.$valid}}</code><br> 43216 * model = <code>{{model}}</code> 43217 * </form> 43218 * </div> 43219 * </file> 43220 * <file name="protractor.js" type="protractor"> 43221 var model = element(by.binding('model')); 43222 var input = element(by.id('input')); 43223 43224 it('should validate the input with the default pattern', function() { 43225 input.sendKeys('aaa'); 43226 expect(model.getText()).not.toContain('aaa'); 43227 43228 input.clear().then(function() { 43229 input.sendKeys('123'); 43230 expect(model.getText()).toContain('123'); 43231 }); 43232 }); 43233 * </file> 43234 * </example> 43235 */ 43236var patternDirective = function() { 43237 return { 43238 restrict: 'A', 43239 require: '?ngModel', 43240 link: function(scope, elm, attr, ctrl) { 43241 if (!ctrl) return; 43242 43243 var regexp, patternExp = attr.ngPattern || attr.pattern; 43244 attr.$observe('pattern', function(regex) { 43245 if (isString(regex) && regex.length > 0) { 43246 regex = new RegExp('^' + regex + '$'); 43247 } 43248 43249 if (regex && !regex.test) { 43250 throw minErr('ngPattern')('noregexp', 43251 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp, 43252 regex, startingTag(elm)); 43253 } 43254 43255 regexp = regex || undefined; 43256 ctrl.$validate(); 43257 }); 43258 43259 ctrl.$validators.pattern = function(modelValue, viewValue) { 43260 // HTML5 pattern constraint validates the input value, so we validate the viewValue 43261 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue); 43262 }; 43263 } 43264 }; 43265}; 43266 43267/** 43268 * @ngdoc directive 43269 * @name ngMaxlength 43270 * 43271 * @description 43272 * 43273 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. 43274 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls. 43275 * 43276 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} 43277 * is longer than the integer obtained by evaluating the Angular expression given in the 43278 * `ngMaxlength` attribute value. 43279 * 43280 * <div class="alert alert-info"> 43281 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two 43282 * differences: 43283 * <ol> 43284 * <li> 43285 * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint 43286 * validation is not available. 43287 * </li> 43288 * <li> 43289 * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be 43290 * interpolated. 43291 * </li> 43292 * </ol> 43293 * </div> 43294 * 43295 * @example 43296 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample"> 43297 * <file name="index.html"> 43298 * <script> 43299 * angular.module('ngMaxlengthExample', []) 43300 * .controller('ExampleController', ['$scope', function($scope) { 43301 * $scope.maxlength = 5; 43302 * }]); 43303 * </script>
43304 * <div ng-controller="ExampleController"> 43305 * <form name="form"> 43306 * <label for="maxlength">Set a maxlength: </label> 43307 * <input type="number" ng-model="maxlength" id="maxlength" /> 43308 * <br> 43309 * <label for="input">This input is restricted by the current maxlength: </label> 43310 * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br> 43311 * <hr> 43312 * input valid? = <code>{{form.input.$valid}}</code><br> 43313 * model = <code>{{model}}</code> 43314 * </form> 43315 * </div> 43316 * </file> 43317 * <file name="protractor.js" type="protractor"> 43318 var model = element(by.binding('model')); 43319 var input = element(by.id('input')); 43320 43321 it('should validate the input with the default maxlength', function() { 43322 input.sendKeys('abcdef'); 43323 expect(model.getText()).not.toContain('abcdef'); 43324 43325 input.clear().then(function() { 43326 input.sendKeys('abcde'); 43327 expect(model.getText()).toContain('abcde'); 43328 }); 43329 }); 43330 * </file> 43331 * </example> 43332 */ 43333var maxlengthDirective = function() { 43334 return { 43335 restrict: 'A', 43336 require: '?ngModel', 43337 link: function(scope, elm, attr, ctrl) { 43338 if (!ctrl) return; 43339 43340 var maxlength = -1; 43341 attr.$observe('maxlength', function(value) { 43342 var intVal = toInt(value); 43343 maxlength = isNumberNaN(intVal) ? -1 : intVal; 43344 ctrl.$validate(); 43345 }); 43346 ctrl.$validators.maxlength = function(modelValue, viewValue) { 43347 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength); 43348 }; 43349 } 43350 }; 43351}; 43352 43353/** 43354 * @ngdoc directive 43355 * @name ngMinlength 43356 * 43357 * @description 43358 * 43359 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. 43360 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls. 43361 * 43362 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} 43363 * is shorter than the integer obtained by evaluating the Angular expression given in the 43364 * `ngMinlength` attribute value. 43365 * 43366 * <div class="alert alert-info"> 43367 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two 43368 * differences: 43369 * <ol> 43370 * <li> 43371 * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint 43372 * validation is not available. 43373 * </li> 43374 * <li> 43375 * The `ngMinlength` value must be an expression, while the `minlength` value must be 43376 * interpolated. 43377 * </li> 43378 * </ol> 43379 * </div> 43380 * 43381 * @example 43382 * <example name="ngMinlengthDirective" module="ngMinlengthExample"> 43383 * <file name="index.html"> 43384 * <script> 43385 * angular.module('ngMinlengthExample', []) 43386 * .controller('ExampleController', ['$scope', function($scope) { 43387 * $scope.minlength = 3; 43388 * }]); 43389 * </script>
43390 * <div ng-controller="ExampleController"> 43391 * <form name="form"> 43392 * <label for="minlength">Set a minlength: </label> 43393 * <input type="number" ng-model="minlength" id="minlength" /> 43394 * <br> 43395 * <label for="input">This input is restricted by the current minlength: </label> 43396 * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br> 43397 * <hr> 43398 * input valid? = <code>{{form.input.$valid}}</code><br> 43399 * model = <code>{{model}}</code> 43400 * </form> 43401 * </div> 43402 * </file> 43403 * <file name="protractor.js" type="protractor"> 43404 var model = element(by.binding('model')); 43405 var input = element(by.id('input')); 43406 43407 it('should validate the input with the default minlength', function() { 43408 input.sendKeys('ab'); 43409 expect(model.getText()).not.toContain('ab'); 43410 43411 input.sendKeys('abc'); 43412 expect(model.getText()).toContain('abc'); 43413 }); 43414 * </file> 43415 * </example> 43416 */ 43417var minlengthDirective = function() { 43418 return { 43419 restrict: 'A', 43420 require: '?ngModel', 43421 link: function(scope, elm, attr, ctrl) { 43422 if (!ctrl) return; 43423 43424 var minlength = 0; 43425 attr.$observe('minlength', function(value) { 43426 minlength = toInt(value) || 0; 43427 ctrl.$validate(); 43428 }); 43429 ctrl.$validators.minlength = function(modelValue, viewValue) { 43430 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength; 43431 }; 43432 } 43433 }; 43434}; 43435 43436if (window.angular.bootstrap) { 43437 // AngularJS is already loaded, so we can return here... 43438 if (window.console) { 43439 console.log('WARNING: Tried to load angular more than once.'); 43440 } 43441 return; 43442} 43443 43444// try to bind to jquery now so that one can write jqLite(fn) 43445// but we will rebind on bootstrap again. 43446bindJQuery(); 43447 43448publishExternalAPI(angular); 43449 43450angular.module("ngLocale", [], ["$provide", function($provide) { 43451var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"}; 43452function getDecimals(n) { 43453 n = n + ''; 43454 var i = n.indexOf('.'); 43455 return (i == -1) ? 0 : n.length - i - 1; 43456} 43457 43458function getVF(n, opt_precision) { 43459 var v = opt_precision; 43460 43461 if (undefined === v) { 43462 v = Math.min(getDecimals(n), 3); 43463 } 43464 43465 var base = Math.pow(10, v); 43466 var f = ((n * base) | 0) % base; 43467 return {v: v, f: f}; 43468} 43469 43470$provide.value("$locale", { 43471 "DATETIME_FORMATS": { 43472 "AMPMS": [ 43473 "AM", 43474 "PM" 43475 ], 43476 "DAY": [ 43477 "Sunday", 43478 "Monday", 43479 "Tuesday", 43480 "Wednesday", 43481 "Thursday", 43482 "Friday", 43483 "Saturday" 43484 ], 43485 "ERANAMES": [ 43486 "Before Christ", 43487 "Anno Domini" 43488 ], 43489 "ERAS": [ 43490 "BC", 43491 "AD" 43492 ], 43493 "FIRSTDAYOFWEEK": 6, 43494 "MONTH": [ 43495 "January", 43496 "February", 43497 "March", 43498 "April", 43499 "May", 43500 "June", 43501 "July", 43502 "August", 43503 "September", 43504 "October", 43505 "November", 43506 "December" 43507 ], 43508 "SHORTDAY": [ 43509 "Sun", 43510 "Mon", 43511 "Tue", 43512 "Wed", 43513 "Thu", 43514 "Fri", 43515 "Sat" 43516 ], 43517 "SHORTMONTH": [ 43518 "Jan", 43519 "Feb", 43520 "Mar", 43521 "Apr", 43522 "May", 43523 "Jun", 43524 "Jul", 43525 "Aug", 43526 "Sep", 43527 "Oct", 43528 "Nov", 43529 "Dec" 43530 ], 43531 "STANDALONEMONTH": [ 43532 "January", 43533 "February", 43534 "March", 43535 "April", 43536 "May", 43537 "June", 43538 "July", 43539 "August", 43540 "September", 43541 "October", 43542 "November", 43543 "December" 43544 ], 43545 "WEEKENDRANGE": [ 43546 5, 43547 6 43548 ], 43549 "fullDate": "EEEE, MMMM d, y", 43550 "longDate": "MMMM d, y", 43551 "medium": "MMM d, y h:mm:ss a", 43552 "mediumDate": "MMM d, y", 43553 "mediumTime": "h:mm:ss a", 43554 "short": "M/d/yy h:mm a", 43555 "shortDate": "M/d/yy", 43556 "shortTime": "h:mm a" 43557 }, 43558 "NUMBER_FORMATS": { 43559 "CURRENCY_SYM": "$", 43560 "DECIMAL_SEP": ".", 43561 "GROUP_SEP": ",", 43562 "PATTERNS": [ 43563 { 43564 "gSize": 3, 43565 "lgSize": 3, 43566 "maxFrac": 3, 43567 "minFrac": 0, 43568 "minInt": 1, 43569 "negPre": "-", 43570 "negSuf": "", 43571 "posPre": "", 43572 "posSuf": "" 43573 }, 43574 { 43575 "gSize": 3, 43576 "lgSize": 3, 43577 "maxFrac": 2, 43578 "minFrac": 2, 43579 "minInt": 1, 43580 "negPre": "-\u00a4", 43581 "negSuf": "", 43582 "posPre": "\u00a4", 43583 "posSuf": "" 43584 } 43585 ] 43586 }, 43587 "id": "en-us", 43588 "localeID": "en_US", 43589 "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;} 43590}); 43591}]); 43592 43593 jqLite(function() { 43594 angularInit(window.document, bootstrap); 43595 }); 43596 43597})(window); 43598 43599!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
vendor: 40,181 bytes, lines 43600-44974
43600/*! 43601 * Bootstrap v3.3.7 (http://getbootstrap.com) 43602 * Copyright 2011-2016 Twitter, Inc. 43603 * Licensed under the MIT license 43604 */ 43605 43606if (typeof jQuery === 'undefined') { 43607 throw new Error('Bootstrap\'s JavaScript requires jQuery') 43608} 43609 43610+function ($) { 43611 'use strict'; 43612 var version = $.fn.jquery.split(' ')[0].split('.') 43613 if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) { 43614 throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4') 43615 } 43616}(jQuery); 43617 43618/* ======================================================================== 43619 * Bootstrap: transition.js v3.3.7 43620 * http://getbootstrap.com/javascript/#transitions 43621 * ======================================================================== 43622 * Copyright 2011-2016 Twitter, Inc. 43623 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 43624 * ======================================================================== */ 43625 43626 43627+function ($) { 43628 'use strict'; 43629 43630 // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) 43631 // ============================================================ 43632 43633 function transitionEnd() { 43634 var el = document.createElement('bootstrap') 43635 43636 var transEndEventNames = { 43637 WebkitTransition : 'webkitTransitionEnd', 43638 MozTransition : 'transitionend', 43639 OTransition : 'oTransitionEnd otransitionend', 43640 transition : 'transitionend' 43641 } 43642 43643 for (var name in transEndEventNames) { 43644 if (el.style[name] !== undefined) { 43645 return { end: transEndEventNames[name] } 43646 } 43647 } 43648 43649 return false // explicit for ie8 ( ._.) 43650 } 43651 43652 // http://blog.alexmaccaw.com/css-transitions 43653 $.fn.emulateTransitionEnd = function (duration) { 43654 var called = false 43655 var $el = this 43656 $(this).one('bsTransitionEnd', function () { called = true }) 43657 var callback = function () { if (!called) $($el).trigger($.support.transition.end) } 43658 setTimeout(callback, duration) 43659 return this 43660 } 43661 43662 $(function () { 43663 $.support.transition = transitionEnd() 43664 43665 if (!$.support.transition) return 43666 43667 $.event.special.bsTransitionEnd = { 43668 bindType: $.support.transition.end, 43669 delegateType: $.support.transition.end, 43670 handle: function (e) { 43671 if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) 43672 } 43673 } 43674 }) 43675 43676}(jQuery); 43677 43678/* ======================================================================== 43679 * Bootstrap: alert.js v3.3.7 43680 * http://getbootstrap.com/javascript/#alerts 43681 * ======================================================================== 43682 * Copyright 2011-2016 Twitter, Inc. 43683 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 43684 * ======================================================================== */ 43685 43686 43687+function ($) { 43688 'use strict'; 43689 43690 // ALERT CLASS DEFINITION 43691 // ====================== 43692 43693 var dismiss = '[data-dismiss="alert"]' 43694 var Alert = function (el) { 43695 $(el).on('click', dismiss, this.close) 43696 } 43697 43698 Alert.VERSION = '3.3.7' 43699 43700 Alert.TRANSITION_DURATION = 150 43701 43702 Alert.prototype.close = function (e) { 43703 var $this = $(this) 43704 var selector = $this.attr('data-target') 43705 43706 if (!selector) { 43707 selector = $this.attr('href') 43708 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 43709 } 43710 43711 var $parent = $(selector === '#' ? [] : selector) 43712 43713 if (e) e.preventDefault() 43714 43715 if (!$parent.length) { 43716 $parent = $this.closest('.alert') 43717 } 43718 43719 $parent.trigger(e = $.Event('close.bs.alert')) 43720 43721 if (e.isDefaultPrevented()) return 43722 43723 $parent.removeClass('in') 43724 43725 function removeElement() { 43726 // detach from parent, fire event then clean up data 43727 $parent.detach().trigger('closed.bs.alert').remove() 43728 } 43729 43730 $.support.transition && $parent.hasClass('fade') ? 43731 $parent 43732 .one('bsTransitionEnd', removeElement) 43733 .emulateTransitionEnd(Alert.TRANSITION_DURATION) : 43734 removeElement() 43735 } 43736 43737 43738 // ALERT PLUGIN DEFINITION 43739 // ======================= 43740 43741 function Plugin(option) { 43742 return this.each(function () { 43743 var $this = $(this) 43744 var data = $this.data('bs.alert') 43745 43746 if (!data) $this.data('bs.alert', (data = new Alert(this))) 43747 if (typeof option == 'string') data[option].call($this) 43748 }) 43749 } 43750 43751 var old = $.fn.alert 43752 43753 $.fn.alert = Plugin 43754 $.fn.alert.Constructor = Alert 43755 43756 43757 // ALERT NO CONFLICT 43758 // ================= 43759 43760 $.fn.alert.noConflict = function () { 43761 $.fn.alert = old 43762 return this 43763 } 43764 43765 43766 // ALERT DATA-API 43767 // ============== 43768 43769 $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) 43770 43771}(jQuery); 43772 43773/* ======================================================================== 43774 * Bootstrap: button.js v3.3.7 43775 * http://getbootstrap.com/javascript/#buttons 43776 * ======================================================================== 43777 * Copyright 2011-2016 Twitter, Inc. 43778 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 43779 * ======================================================================== */ 43780 43781 43782+function ($) { 43783 'use strict'; 43784 43785 // BUTTON PUBLIC CLASS DEFINITION 43786 // ============================== 43787 43788 var Button = function (element, options) { 43789 this.$element = $(element) 43790 this.options = $.extend({}, Button.DEFAULTS, options) 43791 this.isLoading = false 43792 } 43793 43794 Button.VERSION = '3.3.7' 43795 43796 Button.DEFAULTS = { 43797 loadingText: 'loading...' 43798 } 43799 43800 Button.prototype.setState = function (state) { 43801 var d = 'disabled' 43802 var $el = this.$element 43803 var val = $el.is('input') ? 'val' : 'html' 43804 var data = $el.data() 43805 43806 state += 'Text' 43807 43808 if (data.resetText == null) $el.data('resetText', $el[val]()) 43809 43810 // push to event loop to allow forms to submit 43811 setTimeout($.proxy(function () { 43812 $el[val](data[state] == null ? this.options[state] : data[state]) 43813 43814 if (state == 'loadingText') { 43815 this.isLoading = true 43816 $el.addClass(d).attr(d, d).prop(d, true) 43817 } else if (this.isLoading) { 43818 this.isLoading = false 43819 $el.removeClass(d).removeAttr(d).prop(d, false) 43820 } 43821 }, this), 0) 43822 } 43823 43824 Button.prototype.toggle = function () { 43825 var changed = true 43826 var $parent = this.$element.closest('[data-toggle="buttons"]') 43827 43828 if ($parent.length) { 43829 var $input = this.$element.find('input') 43830 if ($input.prop('type') == 'radio') { 43831 if ($input.prop('checked')) changed = false 43832 $parent.find('.active').removeClass('active') 43833 this.$element.addClass('active') 43834 } else if ($input.prop('type') == 'checkbox') { 43835 if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false 43836 this.$element.toggleClass('active') 43837 } 43838 $input.prop('checked', this.$element.hasClass('active')) 43839 if (changed) $input.trigger('change') 43840 } else { 43841 this.$element.attr('aria-pressed', !this.$element.hasClass('active')) 43842 this.$element.toggleClass('active') 43843 } 43844 } 43845 43846 43847 // BUTTON PLUGIN DEFINITION 43848 // ======================== 43849 43850 function Plugin(option) { 43851 return this.each(function () { 43852 var $this = $(this) 43853 var data = $this.data('bs.button') 43854 var options = typeof option == 'object' && option 43855 43856 if (!data) $this.data('bs.button', (data = new Button(this, options))) 43857 43858 if (option == 'toggle') data.toggle() 43859 else if (option) data.setState(option) 43860 }) 43861 } 43862 43863 var old = $.fn.button 43864 43865 $.fn.button = Plugin 43866 $.fn.button.Constructor = Button 43867 43868 43869 // BUTTON NO CONFLICT 43870 // ================== 43871 43872 $.fn.button.noConflict = function () { 43873 $.fn.button = old 43874 return this 43875 } 43876 43877 43878 // BUTTON DATA-API 43879 // =============== 43880 43881 $(document) 43882 .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { 43883 var $btn = $(e.target).closest('.btn') 43884 Plugin.call($btn, 'toggle') 43885 if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) { 43886 // Prevent double click on radios, and the double selections (so cancellation) on checkboxes 43887 e.preventDefault() 43888 // The target component still receive the focus 43889 if ($btn.is('input,button')) $btn.trigger('focus') 43890 else $btn.find('input:visible,button:visible').first().trigger('focus') 43891 } 43892 }) 43893 .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { 43894 $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) 43895 }) 43896 43897}(jQuery); 43898 43899/* ======================================================================== 43900 * Bootstrap: carousel.js v3.3.7 43901 * http://getbootstrap.com/javascript/#carousel 43902 * ======================================================================== 43903 * Copyright 2011-2016 Twitter, Inc. 43904 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 43905 * ======================================================================== */ 43906 43907 43908+function ($) { 43909 'use strict'; 43910 43911 // CAROUSEL CLASS DEFINITION 43912 // ========================= 43913 43914 var Carousel = function (element, options) { 43915 this.$element = $(element) 43916 this.$indicators = this.$element.find('.carousel-indicators') 43917 this.options = options 43918 this.paused = null 43919 this.sliding = null 43920 this.interval = null 43921 this.$active = null 43922 this.$items = null 43923 43924 this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) 43925 43926 this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element 43927 .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) 43928 .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) 43929 } 43930 43931 Carousel.VERSION = '3.3.7' 43932 43933 Carousel.TRANSITION_DURATION = 600 43934 43935 Carousel.DEFAULTS = { 43936 interval: 5000, 43937 pause: 'hover', 43938 wrap: true, 43939 keyboard: true 43940 } 43941 43942 Carousel.prototype.keydown = function (e) { 43943 if (/input|textarea/i.test(e.target.tagName)) return 43944 switch (e.which) { 43945 case 37: this.prev(); break 43946 case 39: this.next(); break 43947 default: return 43948 } 43949 43950 e.preventDefault() 43951 } 43952 43953 Carousel.prototype.cycle = function (e) { 43954 e || (this.paused = false) 43955 43956 this.interval && clearInterval(this.interval) 43957 43958 this.options.interval 43959 && !this.paused 43960 && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 43961 43962 return this 43963 } 43964 43965 Carousel.prototype.getItemIndex = function (item) { 43966 this.$items = item.parent().children('.item') 43967 return this.$items.index(item || this.$active) 43968 } 43969 43970 Carousel.prototype.getItemForDirection = function (direction, active) { 43971 var activeIndex = this.getItemIndex(active) 43972 var willWrap = (direction == 'prev' && activeIndex === 0) 43973 || (direction == 'next' && activeIndex == (this.$items.length - 1)) 43974 if (willWrap && !this.options.wrap) return active 43975 var delta = direction == 'prev' ? -1 : 1 43976 var itemIndex = (activeIndex + delta) % this.$items.length 43977 return this.$items.eq(itemIndex) 43978 } 43979 43980 Carousel.prototype.to = function (pos) { 43981 var that = this 43982 var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) 43983 43984 if (pos > (this.$items.length - 1) || pos < 0) return 43985 43986 if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" 43987 if (activeIndex == pos) return this.pause().cycle() 43988 43989 return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) 43990 } 43991 43992 Carousel.prototype.pause = function (e) { 43993 e || (this.paused = true) 43994 43995 if (this.$element.find('.next, .prev').length && $.support.transition) { 43996 this.$element.trigger($.support.transition.end) 43997 this.cycle(true) 43998 } 43999 44000 this.interval = clearInterval(this.interval) 44001 44002 return this 44003 } 44004 44005 Carousel.prototype.next = function () { 44006 if (this.sliding) return 44007 return this.slide('next') 44008 } 44009 44010 Carousel.prototype.prev = function () { 44011 if (this.sliding) return 44012 return this.slide('prev') 44013 } 44014 44015 Carousel.prototype.slide = function (type, next) { 44016 var $active = this.$element.find('.item.active') 44017 var $next = next || this.getItemForDirection(type, $active) 44018 var isCycling = this.interval 44019 var direction = type == 'next' ? 'left' : 'right' 44020 var that = this 44021 44022 if ($next.hasClass('active')) return (this.sliding = false) 44023 44024 var relatedTarget = $next[0] 44025 var slideEvent = $.Event('slide.bs.carousel', { 44026 relatedTarget: relatedTarget, 44027 direction: direction 44028 }) 44029 this.$element.trigger(slideEvent) 44030 if (slideEvent.isDefaultPrevented()) return 44031 44032 this.sliding = true 44033 44034 isCycling && this.pause() 44035 44036 if (this.$indicators.length) { 44037 this.$indicators.find('.active').removeClass('active') 44038 var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) 44039 $nextIndicator && $nextIndicator.addClass('active') 44040 } 44041 44042 var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" 44043 if ($.support.transition && this.$element.hasClass('slide')) { 44044 $next.addClass(type) 44045 $next[0].offsetWidth // force reflow 44046 $active.addClass(direction) 44047 $next.addClass(direction) 44048 $active 44049 .one('bsTransitionEnd', function () { 44050 $next.removeClass([type, direction].join(' ')).addClass('active') 44051 $active.removeClass(['active', direction].join(' ')) 44052 that.sliding = false 44053 setTimeout(function () { 44054 that.$element.trigger(slidEvent) 44055 }, 0) 44056 }) 44057 .emulateTransitionEnd(Carousel.TRANSITION_DURATION) 44058 } else { 44059 $active.removeClass('active') 44060 $next.addClass('active') 44061 this.sliding = false 44062 this.$element.trigger(slidEvent) 44063 } 44064 44065 isCycling && this.cycle() 44066 44067 return this 44068 } 44069 44070 44071 // CAROUSEL PLUGIN DEFINITION 44072 // ========================== 44073 44074 function Plugin(option) { 44075 return this.each(function () { 44076 var $this = $(this) 44077 var data = $this.data('bs.carousel') 44078 var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) 44079 var action = typeof option == 'string' ? option : options.slide 44080 44081 if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) 44082 if (typeof option == 'number') data.to(option) 44083 else if (action) data[action]() 44084 else if (options.interval) data.pause().cycle() 44085 }) 44086 } 44087 44088 var old = $.fn.carousel 44089 44090 $.fn.carousel = Plugin 44091 $.fn.carousel.Constructor = Carousel 44092 44093 44094 // CAROUSEL NO CONFLICT 44095 // ==================== 44096 44097 $.fn.carousel.noConflict = function () { 44098 $.fn.carousel = old 44099 return this 44100 } 44101 44102 44103 // CAROUSEL DATA-API 44104 // ================= 44105 44106 var clickHandler = function (e) { 44107 var href 44108 var $this = $(this) 44109 var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 44110 if (!$target.hasClass('carousel')) return 44111 var options = $.extend({}, $target.data(), $this.data()) 44112 var slideIndex = $this.attr('data-slide-to') 44113 if (slideIndex) options.interval = false 44114 44115 Plugin.call($target, options) 44116 44117 if (slideIndex) { 44118 $target.data('bs.carousel').to(slideIndex) 44119 } 44120 44121 e.preventDefault() 44122 } 44123 44124 $(document) 44125 .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) 44126 .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) 44127 44128 $(window).on('load', function () { 44129 $('[data-ride="carousel"]').each(function () { 44130 var $carousel = $(this) 44131 Plugin.call($carousel, $carousel.data()) 44132 }) 44133 }) 44134 44135}(jQuery); 44136 44137/* ======================================================================== 44138 * Bootstrap: collapse.js v3.3.7 44139 * http://getbootstrap.com/javascript/#collapse 44140 * ======================================================================== 44141 * Copyright 2011-2016 Twitter, Inc. 44142 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 44143 * ======================================================================== */ 44144 44145/* jshint latedef: false */ 44146 44147+function ($) { 44148 'use strict'; 44149 44150 // COLLAPSE PUBLIC CLASS DEFINITION 44151 // ================================ 44152 44153 var Collapse = function (element, options) { 44154 this.$element = $(element) 44155 this.options = $.extend({}, Collapse.DEFAULTS, options) 44156 this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + 44157 '[data-toggle="collapse"][data-target="#' + element.id + '"]') 44158 this.transitioning = null 44159 44160 if (this.options.parent) { 44161 this.$parent = this.getParent() 44162 } else { 44163 this.addAriaAndCollapsedClass(this.$element, this.$trigger) 44164 } 44165 44166 if (this.options.toggle) this.toggle() 44167 } 44168 44169 Collapse.VERSION = '3.3.7' 44170 44171 Collapse.TRANSITION_DURATION = 350 44172 44173 Collapse.DEFAULTS = { 44174 toggle: true 44175 } 44176 44177 Collapse.prototype.dimension = function () { 44178 var hasWidth = this.$element.hasClass('width') 44179 return hasWidth ? 'width' : 'height' 44180 } 44181 44182 Collapse.prototype.show = function () { 44183 if (this.transitioning || this.$element.hasClass('in')) return 44184 44185 var activesData 44186 var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') 44187 44188 if (actives && actives.length) { 44189 activesData = actives.data('bs.collapse') 44190 if (activesData && activesData.transitioning) return 44191 } 44192 44193 var startEvent = $.Event('show.bs.collapse') 44194 this.$element.trigger(startEvent) 44195 if (startEvent.isDefaultPrevented()) return 44196 44197 if (actives && actives.length) { 44198 Plugin.call(actives, 'hide') 44199 activesData || actives.data('bs.collapse', null) 44200 } 44201 44202 var dimension = this.dimension() 44203 44204 this.$element 44205 .removeClass('collapse') 44206 .addClass('collapsing')[dimension](0) 44207 .attr('aria-expanded', true) 44208 44209 this.$trigger 44210 .removeClass('collapsed') 44211 .attr('aria-expanded', true) 44212 44213 this.transitioning = 1 44214 44215 var complete = function () { 44216 this.$element 44217 .removeClass('collapsing') 44218 .addClass('collapse in')[dimension]('') 44219 this.transitioning = 0 44220 this.$element 44221 .trigger('shown.bs.collapse') 44222 } 44223 44224 if (!$.support.transition) return complete.call(this) 44225 44226 var scrollSize = $.camelCase(['scroll', dimension].join('-')) 44227 44228 this.$element 44229 .one('bsTransitionEnd', $.proxy(complete, this)) 44230 .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) 44231 } 44232 44233 Collapse.prototype.hide = function () { 44234 if (this.transitioning || !this.$element.hasClass('in')) return 44235 44236 var startEvent = $.Event('hide.bs.collapse') 44237 this.$element.trigger(startEvent) 44238 if (startEvent.isDefaultPrevented()) return 44239 44240 var dimension = this.dimension() 44241 44242 this.$element[dimension](this.$element[dimension]())[0].offsetHeight 44243 44244 this.$element 44245 .addClass('collapsing') 44246 .removeClass('collapse in') 44247 .attr('aria-expanded', false) 44248 44249 this.$trigger 44250 .addClass('collapsed') 44251 .attr('aria-expanded', false) 44252 44253 this.transitioning = 1 44254 44255 var complete = function () { 44256 this.transitioning = 0 44257 this.$element 44258 .removeClass('collapsing') 44259 .addClass('collapse') 44260 .trigger('hidden.bs.collapse') 44261 } 44262 44263 if (!$.support.transition) return complete.call(this) 44264 44265 this.$element 44266 [dimension](0) 44267 .one('bsTransitionEnd', $.proxy(complete, this)) 44268 .emulateTransitionEnd(Collapse.TRANSITION_DURATION) 44269 } 44270 44271 Collapse.prototype.toggle = function () { 44272 this[this.$element.hasClass('in') ? 'hide' : 'show']() 44273 } 44274 44275 Collapse.prototype.getParent = function () { 44276 return $(this.options.parent) 44277 .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') 44278 .each($.proxy(function (i, element) { 44279 var $element = $(element) 44280 this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) 44281 }, this)) 44282 .end() 44283 } 44284 44285 Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { 44286 var isOpen = $element.hasClass('in') 44287 44288 $element.attr('aria-expanded', isOpen) 44289 $trigger 44290 .toggleClass('collapsed', !isOpen) 44291 .attr('aria-expanded', isOpen) 44292 } 44293 44294 function getTargetFromTrigger($trigger) { 44295 var href 44296 var target = $trigger.attr('data-target') 44297 || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 44298 44299 return $(target) 44300 } 44301 44302 44303 // COLLAPSE PLUGIN DEFINITION 44304 // ========================== 44305 44306 function Plugin(option) { 44307 return this.each(function () { 44308 var $this = $(this) 44309 var data = $this.data('bs.collapse') 44310 var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) 44311 44312 if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false 44313 if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) 44314 if (typeof option == 'string') data[option]() 44315 }) 44316 } 44317 44318 var old = $.fn.collapse 44319 44320 $.fn.collapse = Plugin 44321 $.fn.collapse.Constructor = Collapse 44322 44323 44324 // COLLAPSE NO CONFLICT 44325 // ==================== 44326 44327 $.fn.collapse.noConflict = function () { 44328 $.fn.collapse = old 44329 return this 44330 } 44331 44332 44333 // COLLAPSE DATA-API 44334 // ================= 44335 44336 $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { 44337 var $this = $(this) 44338 44339 if (!$this.attr('data-target')) e.preventDefault() 44340 44341 var $target = getTargetFromTrigger($this) 44342 var data = $target.data('bs.collapse') 44343 var option = data ? 'toggle' : $this.data() 44344 44345 Plugin.call($target, option) 44346 }) 44347 44348}(jQuery); 44349 44350/* ======================================================================== 44351 * Bootstrap: dropdown.js v3.3.7 44352 * http://getbootstrap.com/javascript/#dropdowns 44353 * ======================================================================== 44354 * Copyright 2011-2016 Twitter, Inc. 44355 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 44356 * ======================================================================== */ 44357 44358 44359+function ($) { 44360 'use strict'; 44361 44362 // DROPDOWN CLASS DEFINITION 44363 // ========================= 44364 44365 var backdrop = '.dropdown-backdrop' 44366 var toggle = '[data-toggle="dropdown"]' 44367 var Dropdown = function (element) { 44368 $(element).on('click.bs.dropdown', this.toggle) 44369 } 44370 44371 Dropdown.VERSION = '3.3.7' 44372 44373 function getParent($this) { 44374 var selector = $this.attr('data-target') 44375 44376 if (!selector) { 44377 selector = $this.attr('href') 44378 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 44379 } 44380 44381 var $parent = selector && $(selector) 44382 44383 return $parent && $parent.length ? $parent : $this.parent() 44384 } 44385 44386 function clearMenus(e) { 44387 if (e && e.which === 3) return 44388 $(backdrop).remove() 44389 $(toggle).each(function () { 44390 var $this = $(this) 44391 var $parent = getParent($this) 44392 var relatedTarget = { relatedTarget: this } 44393 44394 if (!$parent.hasClass('open')) return 44395 44396 if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return 44397 44398 $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) 44399 44400 if (e.isDefaultPrevented()) return 44401 44402 $this.attr('aria-expanded', 'false') 44403 $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) 44404 }) 44405 } 44406 44407 Dropdown.prototype.toggle = function (e) { 44408 var $this = $(this) 44409 44410 if ($this.is('.disabled, :disabled')) return 44411 44412 var $parent = getParent($this) 44413 var isActive = $parent.hasClass('open') 44414 44415 clearMenus() 44416 44417 if (!isActive) { 44418 if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { 44419 // if mobile we use a backdrop because click events don't delegate 44420 $(document.createElement('div')) 44421 .addClass('dropdown-backdrop') 44422 .insertAfter($(this)) 44423 .on('click', clearMenus) 44424 } 44425 44426 var relatedTarget = { relatedTarget: this } 44427 $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) 44428 44429 if (e.isDefaultPrevented()) return 44430 44431 $this 44432 .trigger('focus') 44433 .attr('aria-expanded', 'true') 44434 44435 $parent 44436 .toggleClass('open') 44437 .trigger($.Event('shown.bs.dropdown', relatedTarget)) 44438 } 44439 44440 return false 44441 } 44442 44443 Dropdown.prototype.keydown = function (e) { 44444 if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return 44445 44446 var $this = $(this) 44447 44448 e.preventDefault() 44449 e.stopPropagation() 44450 44451 if ($this.is('.disabled, :disabled')) return 44452 44453 var $parent = getParent($this) 44454 var isActive = $parent.hasClass('open') 44455 44456 if (!isActive && e.which != 27 || isActive && e.which == 27) { 44457 if (e.which == 27) $parent.find(toggle).trigger('focus') 44458 return $this.trigger('click') 44459 } 44460 44461 var desc = ' li:not(.disabled):visible a' 44462 var $items = $parent.find('.dropdown-menu' + desc) 44463 44464 if (!$items.length) return 44465 44466 var index = $items.index(e.target) 44467 44468 if (e.which == 38 && index > 0) index-- // up 44469 if (e.which == 40 && index < $items.length - 1) index++ // down 44470 if (!~index) index = 0 44471 44472 $items.eq(index).trigger('focus') 44473 } 44474 44475 44476 // DROPDOWN PLUGIN DEFINITION 44477 // ========================== 44478 44479 function Plugin(option) { 44480 return this.each(function () { 44481 var $this = $(this) 44482 var data = $this.data('bs.dropdown') 44483 44484 if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) 44485 if (typeof option == 'string') data[option].call($this) 44486 }) 44487 } 44488 44489 var old = $.fn.dropdown 44490 44491 $.fn.dropdown = Plugin 44492 $.fn.dropdown.Constructor = Dropdown 44493 44494 44495 // DROPDOWN NO CONFLICT 44496 // ==================== 44497 44498 $.fn.dropdown.noConflict = function () { 44499 $.fn.dropdown = old 44500 return this 44501 } 44502 44503 44504 // APPLY TO STANDARD DROPDOWN ELEMENTS 44505 // =================================== 44506 44507 $(document) 44508 .on('click.bs.dropdown.data-api', clearMenus) 44509 .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) 44510 .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) 44511 .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) 44512 .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) 44513 44514}(jQuery); 44515 44516/* ======================================================================== 44517 * Bootstrap: modal.js v3.3.7 44518 * http://getbootstrap.com/javascript/#modals 44519 * ======================================================================== 44520 * Copyright 2011-2016 Twitter, Inc. 44521 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 44522 * ======================================================================== */ 44523 44524 44525+function ($) { 44526 'use strict'; 44527 44528 // MODAL CLASS DEFINITION 44529 // ====================== 44530 44531 var Modal = function (element, options) { 44532 this.options = options 44533 this.$body = $(document.body) 44534 this.$element = $(element) 44535 this.$dialog = this.$element.find('.modal-dialog') 44536 this.$backdrop = null 44537 this.isShown = null 44538 this.originalBodyPad = null 44539 this.scrollbarWidth = 0 44540 this.ignoreBackdropClick = false 44541 44542 if (this.options.remote) { 44543 this.$element 44544 .find('.modal-content') 44545 .load(this.options.remote, $.proxy(function () { 44546 this.$element.trigger('loaded.bs.modal') 44547 }, this)) 44548 } 44549 } 44550 44551 Modal.VERSION = '3.3.7' 44552 44553 Modal.TRANSITION_DURATION = 300 44554 Modal.BACKDROP_TRANSITION_DURATION = 150 44555 44556 Modal.DEFAULTS = { 44557 backdrop: true, 44558 keyboard: true, 44559 show: true 44560 } 44561 44562 Modal.prototype.toggle = function (_relatedTarget) { 44563 return this.isShown ? this.hide() : this.show(_relatedTarget) 44564 } 44565 44566 Modal.prototype.show = function (_relatedTarget) { 44567 var that = this 44568 var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) 44569 44570 this.$element.trigger(e) 44571 44572 if (this.isShown || e.isDefaultPrevented()) return 44573 44574 this.isShown = true 44575 44576 this.checkScrollbar() 44577 this.setScrollbar() 44578 this.$body.addClass('modal-open') 44579 44580 this.escape() 44581 this.resize() 44582 44583 this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) 44584 44585 this.$dialog.on('mousedown.dismiss.bs.modal', function () { 44586 that.$element.one('mouseup.dismiss.bs.modal', function (e) { 44587 if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true 44588 }) 44589 }) 44590 44591 this.backdrop(function () { 44592 var transition = $.support.transition && that.$element.hasClass('fade') 44593 44594 if (!that.$element.parent().length) { 44595 that.$element.appendTo(that.$body) // don't move modals dom position 44596 } 44597 44598 that.$element 44599 .show() 44600 .scrollTop(0) 44601 44602 that.adjustDialog() 44603 44604 if (transition) { 44605 that.$element[0].offsetWidth // force reflow 44606 } 44607 44608 that.$element.addClass('in') 44609 44610 that.enforceFocus() 44611 44612 var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) 44613 44614 transition ? 44615 that.$dialog // wait for modal to slide in 44616 .one('bsTransitionEnd', function () { 44617 that.$element.trigger('focus').trigger(e) 44618 }) 44619 .emulateTransitionEnd(Modal.TRANSITION_DURATION) : 44620 that.$element.trigger('focus').trigger(e) 44621 }) 44622 } 44623 44624 Modal.prototype.hide = function (e) { 44625 if (e) e.preventDefault() 44626 44627 e = $.Event('hide.bs.modal') 44628 44629 this.$element.trigger(e) 44630 44631 if (!this.isShown || e.isDefaultPrevented()) return 44632 44633 this.isShown = false 44634 44635 this.escape() 44636 this.resize() 44637 44638 $(document).off('focusin.bs.modal') 44639 44640 this.$element 44641 .removeClass('in') 44642 .off('click.dismiss.bs.modal') 44643 .off('mouseup.dismiss.bs.modal') 44644 44645 this.$dialog.off('mousedown.dismiss.bs.modal') 44646 44647 $.support.transition && this.$element.hasClass('fade') ? 44648 this.$element 44649 .one('bsTransitionEnd', $.proxy(this.hideModal, this)) 44650 .emulateTransitionEnd(Modal.TRANSITION_DURATION) : 44651 this.hideModal() 44652 } 44653 44654 Modal.prototype.enforceFocus = function () { 44655 $(document) 44656 .off('focusin.bs.modal') // guard against infinite focus loop 44657 .on('focusin.bs.modal', $.proxy(function (e) { 44658 if (document !== e.target && 44659 this.$element[0] !== e.target && 44660 !this.$element.has(e.target).length) { 44661 this.$element.trigger('focus') 44662 } 44663 }, this)) 44664 } 44665 44666 Modal.prototype.escape = function () { 44667 if (this.isShown && this.options.keyboard) { 44668 this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { 44669 e.which == 27 && this.hide() 44670 }, this)) 44671 } else if (!this.isShown) { 44672 this.$element.off('keydown.dismiss.bs.modal') 44673 } 44674 } 44675 44676 Modal.prototype.resize = function () { 44677 if (this.isShown) { 44678 $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) 44679 } else { 44680 $(window).off('resize.bs.modal') 44681 } 44682 } 44683 44684 Modal.prototype.hideModal = function () { 44685 var that = this 44686 this.$element.hide() 44687 this.backdrop(function () { 44688 that.$body.removeClass('modal-open') 44689 that.resetAdjustments() 44690 that.resetScrollbar() 44691 that.$element.trigger('hidden.bs.modal') 44692 }) 44693 } 44694 44695 Modal.prototype.removeBackdrop = function () { 44696 this.$backdrop && this.$backdrop.remove() 44697 this.$backdrop = null 44698 } 44699 44700 Modal.prototype.backdrop = function (callback) { 44701 var that = this 44702 var animate = this.$element.hasClass('fade') ? 'fade' : '' 44703 44704 if (this.isShown && this.options.backdrop) { 44705 var doAnimate = $.support.transition && animate 44706 44707 this.$backdrop = $(document.createElement('div')) 44708 .addClass('modal-backdrop ' + animate) 44709 .appendTo(this.$body) 44710 44711 this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { 44712 if (this.ignoreBackdropClick) { 44713 this.ignoreBackdropClick = false 44714 return 44715 } 44716 if (e.target !== e.currentTarget) return 44717 this.options.backdrop == 'static' 44718 ? this.$element[0].focus() 44719 : this.hide() 44720 }, this)) 44721 44722 if (doAnimate) this.$backdrop[0].offsetWidth // force reflow 44723 44724 this.$backdrop.addClass('in') 44725 44726 if (!callback) return 44727 44728 doAnimate ? 44729 this.$backdrop 44730 .one('bsTransitionEnd', callback) 44731 .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : 44732 callback() 44733 44734 } else if (!this.isShown && this.$backdrop) { 44735 this.$backdrop.removeClass('in') 44736 44737 var callbackRemove = function () { 44738 that.removeBackdrop() 44739 callback && callback() 44740 } 44741 $.support.transition && this.$element.hasClass('fade') ? 44742 this.$backdrop 44743 .one('bsTransitionEnd', callbackRemove) 44744 .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : 44745 callbackRemove() 44746 44747 } else if (callback) { 44748 callback() 44749 } 44750 } 44751 44752 // these following methods are used to handle overflowing modals 44753 44754 Modal.prototype.handleUpdate = function () { 44755 this.adjustDialog() 44756 } 44757 44758 Modal.prototype.adjustDialog = function () { 44759 var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight 44760 44761 this.$element.css({ 44762 paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', 44763 paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' 44764 }) 44765 } 44766 44767 Modal.prototype.resetAdjustments = function () { 44768 this.$element.css({ 44769 paddingLeft: '', 44770 paddingRight: '' 44771 }) 44772 } 44773 44774 Modal.prototype.checkScrollbar = function () { 44775 var fullWindowWidth = window.innerWidth 44776 if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 44777 var documentElementRect = document.documentElement.getBoundingClientRect() 44778 fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) 44779 } 44780 this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth 44781 this.scrollbarWidth = this.measureScrollbar() 44782 } 44783 44784 Modal.prototype.setScrollbar = function () { 44785 var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) 44786 this.originalBodyPad = document.body.style.paddingRight || '' 44787 if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) 44788 } 44789 44790 Modal.prototype.resetScrollbar = function () { 44791 this.$body.css('padding-right', this.originalBodyPad) 44792 } 44793 44794 Modal.prototype.measureScrollbar = function () { // thx walsh 44795 var scrollDiv = document.createElement('div') 44796 scrollDiv.className = 'modal-scrollbar-measure' 44797 this.$body.append(scrollDiv) 44798 var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth 44799 this.$body[0].removeChild(scrollDiv) 44800 return scrollbarWidth 44801 } 44802 44803 44804 // MODAL PLUGIN DEFINITION 44805 // ======================= 44806 44807 function Plugin(option, _relatedTarget) { 44808 return this.each(function () { 44809 var $this = $(this) 44810 var data = $this.data('bs.modal') 44811 var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) 44812 44813 if (!data) $this.data('bs.modal', (data = new Modal(this, options))) 44814 if (typeof option == 'string') data[option](_relatedTarget) 44815 else if (options.show) data.show(_relatedTarget) 44816 }) 44817 } 44818 44819 var old = $.fn.modal 44820 44821 $.fn.modal = Plugin 44822 $.fn.modal.Constructor = Modal 44823 44824 44825 // MODAL NO CONFLICT 44826 // ================= 44827 44828 $.fn.modal.noConflict = function () { 44829 $.fn.modal = old 44830 return this 44831 } 44832 44833 44834 // MODAL DATA-API 44835 // ============== 44836 44837 $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { 44838 var $this = $(this) 44839 var href = $this.attr('href') 44840 var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 44841 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) 44842 44843 if ($this.is('a')) e.preventDefault() 44844 44845 $target.one('show.bs.modal', function (showEvent) { 44846 if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown 44847 $target.one('hidden.bs.modal', function () { 44848 $this.is(':visible') && $this.trigger('focus') 44849 }) 44850 }) 44851 Plugin.call($target, option, this) 44852 }) 44853 44854}(jQuery); 44855 44856/* ======================================================================== 44857 * Bootstrap: tooltip.js v3.3.7 44858 * http://getbootstrap.com/javascript/#tooltip 44859 * Inspired by the original jQuery.tipsy by Jason Frame 44860 * ======================================================================== 44861 * Copyright 2011-2016 Twitter, Inc. 44862 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 44863 * ======================================================================== */ 44864 44865 44866+function ($) { 44867 'use strict'; 44868 44869 // TOOLTIP PUBLIC CLASS DEFINITION 44870 // =============================== 44871 44872 var Tooltip = function (element, options) { 44873 this.type = null 44874 this.options = null 44875 this.enabled = null 44876 this.timeout = null 44877 this.hoverState = null 44878 this.$element = null 44879 this.inState = null 44880 44881 this.init('tooltip', element, options) 44882 } 44883 44884 Tooltip.VERSION = '3.3.7' 44885 44886 Tooltip.TRANSITION_DURATION = 150 44887 44888 Tooltip.DEFAULTS = { 44889 animation: true, 44890 placement: 'top', 44891 selector: false, 44892 template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', 44893 trigger: 'hover focus', 44894 title: '', 44895 delay: 0, 44896 html: false, 44897 container: false, 44898 viewport: { 44899 selector: 'body', 44900 padding: 0 44901 } 44902 } 44903 44904 Tooltip.prototype.init = function (type, element, options) { 44905 this.enabled = true 44906 this.type = type 44907 this.$element = $(element) 44908 this.options = this.getOptions(options) 44909 this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) 44910 this.inState = { click: false, hover: false, focus: false } 44911 44912 if (this.$element[0] instanceof document.constructor && !this.options.selector) { 44913 throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') 44914 } 44915 44916 var triggers = this.options.trigger.split(' ') 44917 44918 for (var i = triggers.length; i--;) { 44919 var trigger = triggers[i] 44920 44921 if (trigger == 'click') { 44922 this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) 44923 } else if (trigger != 'manual') { 44924 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' 44925 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' 44926 44927 this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) 44928 this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) 44929 } 44930 } 44931 44932 this.options.selector ? 44933 (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 44934 this.fixTitle() 44935 } 44936 44937 Tooltip.prototype.getDefaults = function () { 44938 return Tooltip.DEFAULTS 44939 } 44940 44941 Tooltip.prototype.getOptions = function (options) { 44942 options = $.extend({}, this.getDefaults(), this.$element.data(), options) 44943 44944 if (options.delay && typeof options.delay == 'number') { 44945 options.delay = { 44946 show: options.delay, 44947 hide: options.delay 44948 } 44949 } 44950 44951 return options 44952 } 44953 44954 Tooltip.prototype.getDelegateOptions = function () { 44955 var options = {} 44956 var defaults = this.getDefaults() 44957 44958 this._options && $.each(this._options, function (key, value) { 44959 if (defaults[key] != value) options[key] = value 44960 }) 44961 44962 return options 44963 } 44964 44965 Tooltip.prototype.enter = function (obj) { 44966 var self = obj instanceof this.constructor ? 44967 obj : $(obj.currentTarget).data('bs.' + this.type) 44968 44969 if (!self) { 44970 self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) 44971 $(obj.currentTarget).data('bs.' + this.type, self) 44972 } 44973 44974 if (obj instanceof $.Event) {
vendor: 9,753 bytes, lines 44975-45253
44975 self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true 44976 } 44977 44978 if (self.tip().hasClass('in') || self.hoverState == 'in') { 44979 self.hoverState = 'in' 44980 return 44981 } 44982 44983 clearTimeout(self.timeout) 44984 44985 self.hoverState = 'in' 44986 44987 if (!self.options.delay || !self.options.delay.show) return self.show() 44988 44989 self.timeout = setTimeout(function () { 44990 if (self.hoverState == 'in') self.show() 44991 }, self.options.delay.show) 44992 } 44993 44994 Tooltip.prototype.isInStateTrue = function () { 44995 for (var key in this.inState) { 44996 if (this.inState[key]) return true 44997 } 44998 44999 return false 45000 } 45001 45002 Tooltip.prototype.leave = function (obj) { 45003 var self = obj instanceof this.constructor ? 45004 obj : $(obj.currentTarget).data('bs.' + this.type) 45005 45006 if (!self) { 45007 self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) 45008 $(obj.currentTarget).data('bs.' + this.type, self) 45009 } 45010 45011 if (obj instanceof $.Event) { 45012 self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false 45013 } 45014 45015 if (self.isInStateTrue()) return 45016 45017 clearTimeout(self.timeout) 45018 45019 self.hoverState = 'out' 45020 45021 if (!self.options.delay || !self.options.delay.hide) return self.hide() 45022 45023 self.timeout = setTimeout(function () { 45024 if (self.hoverState == 'out') self.hide() 45025 }, self.options.delay.hide) 45026 } 45027 45028 Tooltip.prototype.show = function () { 45029 var e = $.Event('show.bs.' + this.type) 45030 45031 if (this.hasContent() && this.enabled) { 45032 this.$element.trigger(e) 45033 45034 var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) 45035 if (e.isDefaultPrevented() || !inDom) return 45036 var that = this 45037 45038 var $tip = this.tip() 45039 45040 var tipId = this.getUID(this.type) 45041 45042 this.setContent() 45043 $tip.attr('id', tipId) 45044 this.$element.attr('aria-describedby', tipId) 45045 45046 if (this.options.animation) $tip.addClass('fade') 45047 45048 var placement = typeof this.options.placement == 'function' ? 45049 this.options.placement.call(this, $tip[0], this.$element[0]) : 45050 this.options.placement 45051 45052 var autoToken = /\s?auto?\s?/i 45053 var autoPlace = autoToken.test(placement) 45054 if (autoPlace) placement = placement.replace(autoToken, '') || 'top' 45055 45056 $tip 45057 .detach() 45058 .css({ top: 0, left: 0, display: 'block' }) 45059 .addClass(placement) 45060 .data('bs.' + this.type, this) 45061 45062 this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) 45063 this.$element.trigger('inserted.bs.' + this.type) 45064 45065 var pos = this.getPosition() 45066 var actualWidth = $tip[0].offsetWidth 45067 var actualHeight = $tip[0].offsetHeight 45068 45069 if (autoPlace) { 45070 var orgPlacement = placement 45071 var viewportDim = this.getPosition(this.$viewport) 45072 45073 placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : 45074 placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : 45075 placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : 45076 placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : 45077 placement 45078 45079 $tip 45080 .removeClass(orgPlacement) 45081 .addClass(placement) 45082 } 45083 45084 var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) 45085 45086 this.applyPlacement(calculatedOffset, placement) 45087 45088 var complete = function () { 45089 var prevHoverState = that.hoverState 45090 that.$element.trigger('shown.bs.' + that.type) 45091 that.hoverState = null 45092 45093 if (prevHoverState == 'out') that.leave(that) 45094 } 45095 45096 $.support.transition && this.$tip.hasClass('fade') ? 45097 $tip 45098 .one('bsTransitionEnd', complete) 45099 .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : 45100 complete() 45101 } 45102 } 45103 45104 Tooltip.prototype.applyPlacement = function (offset, placement) { 45105 var $tip = this.tip() 45106 var width = $tip[0].offsetWidth 45107 var height = $tip[0].offsetHeight 45108 45109 // manually read margins because getBoundingClientRect includes difference 45110 var marginTop = parseInt($tip.css('margin-top'), 10) 45111 var marginLeft = parseInt($tip.css('margin-left'), 10) 45112 45113 // we must check for NaN for ie 8/9 45114 if (isNaN(marginTop)) marginTop = 0 45115 if (isNaN(marginLeft)) marginLeft = 0 45116 45117 offset.top += marginTop 45118 offset.left += marginLeft 45119 45120 // $.fn.offset doesn't round pixel values 45121 // so we use setOffset directly with our own function B-0 45122 $.offset.setOffset($tip[0], $.extend({ 45123 using: function (props) { 45124 $tip.css({ 45125 top: Math.round(props.top), 45126 left: Math.round(props.left) 45127 }) 45128 } 45129 }, offset), 0) 45130 45131 $tip.addClass('in') 45132 45133 // check to see if placing tip in new offset caused the tip to resize itself 45134 var actualWidth = $tip[0].offsetWidth 45135 var actualHeight = $tip[0].offsetHeight 45136 45137 if (placement == 'top' && actualHeight != height) { 45138 offset.top = offset.top + height - actualHeight 45139 } 45140 45141 var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) 45142 45143 if (delta.left) offset.left += delta.left 45144 else offset.top += delta.top 45145 45146 var isVertical = /top|bottom/.test(placement) 45147 var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight 45148 var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' 45149 45150 $tip.offset(offset) 45151 this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) 45152 } 45153 45154 Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { 45155 this.arrow() 45156 .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') 45157 .css(isVertical ? 'top' : 'left', '') 45158 } 45159 45160 Tooltip.prototype.setContent = function () { 45161 var $tip = this.tip() 45162 var title = this.getTitle() 45163 45164 $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) 45165 $tip.removeClass('fade in top bottom left right') 45166 } 45167 45168 Tooltip.prototype.hide = function (callback) { 45169 var that = this 45170 var $tip = $(this.$tip) 45171 var e = $.Event('hide.bs.' + this.type) 45172 45173 function complete() { 45174 if (that.hoverState != 'in') $tip.detach() 45175 if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary. 45176 that.$element 45177 .removeAttr('aria-describedby') 45178 .trigger('hidden.bs.' + that.type) 45179 } 45180 callback && callback() 45181 } 45182 45183 this.$element.trigger(e) 45184 45185 if (e.isDefaultPrevented()) return 45186 45187 $tip.removeClass('in') 45188 45189 $.support.transition && $tip.hasClass('fade') ? 45190 $tip 45191 .one('bsTransitionEnd', complete) 45192 .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : 45193 complete() 45194 45195 this.hoverState = null 45196 45197 return this 45198 } 45199 45200 Tooltip.prototype.fixTitle = function () { 45201 var $e = this.$element 45202 if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { 45203 $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') 45204 } 45205 } 45206 45207 Tooltip.prototype.hasContent = function () { 45208 return this.getTitle() 45209 } 45210 45211 Tooltip.prototype.getPosition = function ($element) { 45212 $element = $element || this.$element 45213 45214 var el = $element[0] 45215 var isBody = el.tagName == 'BODY' 45216 45217 var elRect = el.getBoundingClientRect() 45218 if (elRect.width == null) { 45219 // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 45220 elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) 45221 } 45222 var isSvg = window.SVGElement && el instanceof window.SVGElement 45223 // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3. 45224 // See https://github.com/twbs/bootstrap/issues/20280 45225 var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset()) 45226 var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } 45227 var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null 45228 45229 return $.extend({}, elRect, scroll, outerDims, elOffset) 45230 } 45231 45232 Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { 45233 return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : 45234 placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : 45235 placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : 45236 /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } 45237 45238 } 45239 45240 Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { 45241 var delta = { top: 0, left: 0 } 45242 if (!this.$viewport) return delta 45243 45244 var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 45245 var viewportDimensions = this.getPosition(this.$viewport) 45246 45247 if (/right|left/.test(placement)) { 45248 var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll 45249 var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight 45250 if (topEdgeOffset < viewportDimensions.top) { // top overflow 45251 delta.top = viewportDimensions.top - topEdgeOffset 45252 } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow 45253 delta.top = viewportDimensions.top + viewportDimensions.height - bottom
vendor: 19,773 bytes, lines 45253-45976
45253EdgeOffset 45254 } 45255 } else { 45256 var leftEdgeOffset = pos.left - viewportPadding 45257 var rightEdgeOffset = pos.left + viewportPadding + actualWidth 45258 if (leftEdgeOffset < viewportDimensions.left) { // left overflow 45259 delta.left = viewportDimensions.left - leftEdgeOffset 45260 } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow 45261 delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset 45262 } 45263 } 45264 45265 return delta 45266 } 45267 45268 Tooltip.prototype.getTitle = function () { 45269 var title 45270 var $e = this.$element 45271 var o = this.options 45272 45273 title = $e.attr('data-original-title') 45274 || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) 45275 45276 return title 45277 } 45278 45279 Tooltip.prototype.getUID = function (prefix) { 45280 do prefix += ~~(Math.random() * 1000000) 45281 while (document.getElementById(prefix)) 45282 return prefix 45283 } 45284 45285 Tooltip.prototype.tip = function () { 45286 if (!this.$tip) { 45287 this.$tip = $(this.options.template) 45288 if (this.$tip.length != 1) { 45289 throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') 45290 } 45291 } 45292 return this.$tip 45293 } 45294 45295 Tooltip.prototype.arrow = function () { 45296 return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) 45297 } 45298 45299 Tooltip.prototype.enable = function () { 45300 this.enabled = true 45301 } 45302 45303 Tooltip.prototype.disable = function () { 45304 this.enabled = false 45305 } 45306 45307 Tooltip.prototype.toggleEnabled = function () { 45308 this.enabled = !this.enabled 45309 } 45310 45311 Tooltip.prototype.toggle = function (e) { 45312 var self = this 45313 if (e) { 45314 self = $(e.currentTarget).data('bs.' + this.type) 45315 if (!self) { 45316 self = new this.constructor(e.currentTarget, this.getDelegateOptions()) 45317 $(e.currentTarget).data('bs.' + this.type, self) 45318 } 45319 } 45320 45321 if (e) { 45322 self.inState.click = !self.inState.click 45323 if (self.isInStateTrue()) self.enter(self) 45324 else self.leave(self) 45325 } else { 45326 self.tip().hasClass('in') ? self.leave(self) : self.enter(self) 45327 } 45328 } 45329 45330 Tooltip.prototype.destroy = function () { 45331 var that = this 45332 clearTimeout(this.timeout) 45333 this.hide(function () { 45334 that.$element.off('.' + that.type).removeData('bs.' + that.type) 45335 if (that.$tip) { 45336 that.$tip.detach() 45337 } 45338 that.$tip = null 45339 that.$arrow = null 45340 that.$viewport = null 45341 that.$element = null 45342 }) 45343 } 45344 45345 45346 // TOOLTIP PLUGIN DEFINITION 45347 // ========================= 45348 45349 function Plugin(option) { 45350 return this.each(function () { 45351 var $this = $(this) 45352 var data = $this.data('bs.tooltip') 45353 var options = typeof option == 'object' && option 45354 45355 if (!data && /destroy|hide/.test(option)) return 45356 if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) 45357 if (typeof option == 'string') data[option]() 45358 }) 45359 } 45360 45361 var old = $.fn.tooltip 45362 45363 $.fn.tooltip = Plugin 45364 $.fn.tooltip.Constructor = Tooltip 45365 45366 45367 // TOOLTIP NO CONFLICT 45368 // =================== 45369 45370 $.fn.tooltip.noConflict = function () { 45371 $.fn.tooltip = old 45372 return this 45373 } 45374 45375}(jQuery); 45376 45377/* ======================================================================== 45378 * Bootstrap: popover.js v3.3.7 45379 * http://getbootstrap.com/javascript/#popovers 45380 * ======================================================================== 45381 * Copyright 2011-2016 Twitter, Inc. 45382 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 45383 * ======================================================================== */ 45384 45385 45386+function ($) { 45387 'use strict'; 45388 45389 // POPOVER PUBLIC CLASS DEFINITION 45390 // =============================== 45391 45392 var Popover = function (element, options) { 45393 this.init('popover', element, options) 45394 } 45395 45396 if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') 45397 45398 Popover.VERSION = '3.3.7' 45399 45400 Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { 45401 placement: 'right', 45402 trigger: 'click', 45403 content: '', 45404 template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' 45405 }) 45406 45407 45408 // NOTE: POPOVER EXTENDS tooltip.js 45409 // ================================ 45410 45411 Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) 45412 45413 Popover.prototype.constructor = Popover 45414 45415 Popover.prototype.getDefaults = function () { 45416 return Popover.DEFAULTS 45417 } 45418 45419 Popover.prototype.setContent = function () { 45420 var $tip = this.tip() 45421 var title = this.getTitle() 45422 var content = this.getContent() 45423 45424 $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) 45425 $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events 45426 this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' 45427 ](content) 45428 45429 $tip.removeClass('fade top bottom left right in') 45430 45431 // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do 45432 // this manually by checking the contents. 45433 if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() 45434 } 45435 45436 Popover.prototype.hasContent = function () { 45437 return this.getTitle() || this.getContent() 45438 } 45439 45440 Popover.prototype.getContent = function () { 45441 var $e = this.$element 45442 var o = this.options 45443 45444 return $e.attr('data-content') 45445 || (typeof o.content == 'function' ? 45446 o.content.call($e[0]) : 45447 o.content) 45448 } 45449 45450 Popover.prototype.arrow = function () { 45451 return (this.$arrow = this.$arrow || this.tip().find('.arrow')) 45452 } 45453 45454 45455 // POPOVER PLUGIN DEFINITION 45456 // ========================= 45457 45458 function Plugin(option) { 45459 return this.each(function () { 45460 var $this = $(this) 45461 var data = $this.data('bs.popover') 45462 var options = typeof option == 'object' && option 45463 45464 if (!data && /destroy|hide/.test(option)) return 45465 if (!data) $this.data('bs.popover', (data = new Popover(this, options))) 45466 if (typeof option == 'string') data[option]() 45467 }) 45468 } 45469 45470 var old = $.fn.popover 45471 45472 $.fn.popover = Plugin 45473 $.fn.popover.Constructor = Popover 45474 45475 45476 // POPOVER NO CONFLICT 45477 // =================== 45478 45479 $.fn.popover.noConflict = function () { 45480 $.fn.popover = old 45481 return this 45482 } 45483 45484}(jQuery); 45485 45486/* ======================================================================== 45487 * Bootstrap: scrollspy.js v3.3.7 45488 * http://getbootstrap.com/javascript/#scrollspy 45489 * ======================================================================== 45490 * Copyright 2011-2016 Twitter, Inc. 45491 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 45492 * ======================================================================== */ 45493 45494 45495+function ($) { 45496 'use strict'; 45497 45498 // SCROLLSPY CLASS DEFINITION 45499 // ========================== 45500 45501 function ScrollSpy(element, options) { 45502 this.$body = $(document.body) 45503 this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) 45504 this.options = $.extend({}, ScrollSpy.DEFAULTS, options) 45505 this.selector = (this.options.target || '') + ' .nav li > a' 45506 this.offsets = [] 45507 this.targets = [] 45508 this.activeTarget = null 45509 this.scrollHeight = 0 45510 45511 this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) 45512 this.refresh() 45513 this.process() 45514 } 45515 45516 ScrollSpy.VERSION = '3.3.7' 45517 45518 ScrollSpy.DEFAULTS = { 45519 offset: 10 45520 } 45521 45522 ScrollSpy.prototype.getScrollHeight = function () { 45523 return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) 45524 } 45525 45526 ScrollSpy.prototype.refresh = function () { 45527 var that = this 45528 var offsetMethod = 'offset' 45529 var offsetBase = 0 45530 45531 this.offsets = [] 45532 this.targets = [] 45533 this.scrollHeight = this.getScrollHeight() 45534 45535 if (!$.isWindow(this.$scrollElement[0])) { 45536 offsetMethod = 'position' 45537 offsetBase = this.$scrollElement.scrollTop() 45538 } 45539 45540 this.$body 45541 .find(this.selector) 45542 .map(function () { 45543 var $el = $(this) 45544 var href = $el.data('target') || $el.attr('href') 45545 var $href = /^#./.test(href) && $(href) 45546 45547 return ($href 45548 && $href.length 45549 && $href.is(':visible') 45550 && [[$href[offsetMethod]().top + offsetBase, href]]) || null 45551 }) 45552 .sort(function (a, b) { return a[0] - b[0] }) 45553 .each(function () { 45554 that.offsets.push(this[0]) 45555 that.targets.push(this[1]) 45556 }) 45557 } 45558 45559 ScrollSpy.prototype.process = function () { 45560 var scrollTop = this.$scrollElement.scrollTop() + this.options.offset 45561 var scrollHeight = this.getScrollHeight() 45562 var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() 45563 var offsets = this.offsets 45564 var targets = this.targets 45565 var activeTarget = this.activeTarget 45566 var i 45567 45568 if (this.scrollHeight != scrollHeight) { 45569 this.refresh() 45570 } 45571 45572 if (scrollTop >= maxScroll) { 45573 return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) 45574 } 45575 45576 if (activeTarget && scrollTop < offsets[0]) { 45577 this.activeTarget = null 45578 return this.clear() 45579 } 45580 45581 for (i = offsets.length; i--;) { 45582 activeTarget != targets[i] 45583 && scrollTop >= offsets[i] 45584 && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) 45585 && this.activate(targets[i]) 45586 } 45587 } 45588 45589 ScrollSpy.prototype.activate = function (target) { 45590 this.activeTarget = target 45591 45592 this.clear() 45593 45594 var selector = this.selector + 45595 '[data-target="' + target + '"],' + 45596 this.selector + '[href="' + target + '"]' 45597 45598 var active = $(selector) 45599 .parents('li') 45600 .addClass('active') 45601 45602 if (active.parent('.dropdown-menu').length) { 45603 active = active 45604 .closest('li.dropdown') 45605 .addClass('active') 45606 } 45607 45608 active.trigger('activate.bs.scrollspy') 45609 } 45610 45611 ScrollSpy.prototype.clear = function () { 45612 $(this.selector) 45613 .parentsUntil(this.options.target, '.active') 45614 .removeClass('active') 45615 } 45616 45617 45618 // SCROLLSPY PLUGIN DEFINITION 45619 // =========================== 45620 45621 function Plugin(option) { 45622 return this.each(function () { 45623 var $this = $(this) 45624 var data = $this.data('bs.scrollspy') 45625 var options = typeof option == 'object' && option 45626 45627 if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) 45628 if (typeof option == 'string') data[option]() 45629 }) 45630 } 45631 45632 var old = $.fn.scrollspy 45633 45634 $.fn.scrollspy = Plugin 45635 $.fn.scrollspy.Constructor = ScrollSpy 45636 45637 45638 // SCROLLSPY NO CONFLICT 45639 // ===================== 45640 45641 $.fn.scrollspy.noConflict = function () { 45642 $.fn.scrollspy = old 45643 return this 45644 } 45645 45646 45647 // SCROLLSPY DATA-API 45648 // ================== 45649 45650 $(window).on('load.bs.scrollspy.data-api', function () { 45651 $('[data-spy="scroll"]').each(function () { 45652 var $spy = $(this) 45653 Plugin.call($spy, $spy.data()) 45654 }) 45655 }) 45656 45657}(jQuery); 45658 45659/* ======================================================================== 45660 * Bootstrap: tab.js v3.3.7 45661 * http://getbootstrap.com/javascript/#tabs 45662 * ======================================================================== 45663 * Copyright 2011-2016 Twitter, Inc. 45664 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 45665 * ======================================================================== */ 45666 45667 45668+function ($) { 45669 'use strict'; 45670 45671 // TAB CLASS DEFINITION 45672 // ==================== 45673 45674 var Tab = function (element) { 45675 // jscs:disable requireDollarBeforejQueryAssignment 45676 this.element = $(element) 45677 // jscs:enable requireDollarBeforejQueryAssignment 45678 } 45679 45680 Tab.VERSION = '3.3.7' 45681 45682 Tab.TRANSITION_DURATION = 150 45683 45684 Tab.prototype.show = function () { 45685 var $this = this.element 45686 var $ul = $this.closest('ul:not(.dropdown-menu)') 45687 var selector = $this.data('target') 45688 45689 if (!selector) { 45690 selector = $this.attr('href') 45691 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 45692 } 45693 45694 if ($this.parent('li').hasClass('active')) return 45695 45696 var $previous = $ul.find('.active:last a') 45697 var hideEvent = $.Event('hide.bs.tab', { 45698 relatedTarget: $this[0] 45699 }) 45700 var showEvent = $.Event('show.bs.tab', { 45701 relatedTarget: $previous[0] 45702 }) 45703 45704 $previous.trigger(hideEvent) 45705 $this.trigger(showEvent) 45706 45707 if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return 45708 45709 var $target = $(selector) 45710 45711 this.activate($this.closest('li'), $ul) 45712 this.activate($target, $target.parent(), function () { 45713 $previous.trigger({ 45714 type: 'hidden.bs.tab', 45715 relatedTarget: $this[0] 45716 }) 45717 $this.trigger({ 45718 type: 'shown.bs.tab', 45719 relatedTarget: $previous[0] 45720 }) 45721 }) 45722 } 45723 45724 Tab.prototype.activate = function (element, container, callback) { 45725 var $active = container.find('> .active') 45726 var transition = callback 45727 && $.support.transition 45728 && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) 45729 45730 function next() { 45731 $active 45732 .removeClass('active') 45733 .find('> .dropdown-menu > .active') 45734 .removeClass('active') 45735 .end() 45736 .find('[data-toggle="tab"]') 45737 .attr('aria-expanded', false) 45738 45739 element 45740 .addClass('active') 45741 .find('[data-toggle="tab"]') 45742 .attr('aria-expanded', true) 45743 45744 if (transition) { 45745 element[0].offsetWidth // reflow for transition 45746 element.addClass('in') 45747 } else { 45748 element.removeClass('fade') 45749 } 45750 45751 if (element.parent('.dropdown-menu').length) { 45752 element 45753 .closest('li.dropdown') 45754 .addClass('active') 45755 .end() 45756 .find('[data-toggle="tab"]') 45757 .attr('aria-expanded', true) 45758 } 45759 45760 callback && callback() 45761 } 45762 45763 $active.length && transition ? 45764 $active 45765 .one('bsTransitionEnd', next) 45766 .emulateTransitionEnd(Tab.TRANSITION_DURATION) : 45767 next() 45768 45769 $active.removeClass('in') 45770 } 45771 45772 45773 // TAB PLUGIN DEFINITION 45774 // ===================== 45775 45776 function Plugin(option) { 45777 return this.each(function () { 45778 var $this = $(this) 45779 var data = $this.data('bs.tab') 45780 45781 if (!data) $this.data('bs.tab', (data = new Tab(this))) 45782 if (typeof option == 'string') data[option]() 45783 }) 45784 } 45785 45786 var old = $.fn.tab 45787 45788 $.fn.tab = Plugin 45789 $.fn.tab.Constructor = Tab 45790 45791 45792 // TAB NO CONFLICT 45793 // =============== 45794 45795 $.fn.tab.noConflict = function () { 45796 $.fn.tab = old 45797 return this 45798 } 45799 45800 45801 // TAB DATA-API 45802 // ============ 45803 45804 var clickHandler = function (e) { 45805 e.preventDefault() 45806 Plugin.call($(this), 'show') 45807 } 45808 45809 $(document) 45810 .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) 45811 .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) 45812 45813}(jQuery); 45814 45815/* ======================================================================== 45816 * Bootstrap: affix.js v3.3.7 45817 * http://getbootstrap.com/javascript/#affix 45818 * ======================================================================== 45819 * Copyright 2011-2016 Twitter, Inc. 45820 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 45821 * ======================================================================== */ 45822 45823 45824+function ($) { 45825 'use strict'; 45826 45827 // AFFIX CLASS DEFINITION 45828 // ====================== 45829 45830 var Affix = function (element, options) { 45831 this.options = $.extend({}, Affix.DEFAULTS, options) 45832 45833 this.$target = $(this.options.target) 45834 .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) 45835 .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) 45836 45837 this.$element = $(element) 45838 this.affixed = null 45839 this.unpin = null 45840 this.pinnedOffset = null 45841 45842 this.checkPosition() 45843 } 45844 45845 Affix.VERSION = '3.3.7' 45846 45847 Affix.RESET = 'affix affix-top affix-bottom' 45848 45849 Affix.DEFAULTS = { 45850 offset: 0, 45851 target: window 45852 } 45853 45854 Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { 45855 var scrollTop = this.$target.scrollTop() 45856 var position = this.$element.offset() 45857 var targetHeight = this.$target.height() 45858 45859 if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false 45860 45861 if (this.affixed == 'bottom') { 45862 if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' 45863 return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' 45864 } 45865 45866 var initializing = this.affixed == null 45867 var colliderTop = initializing ? scrollTop : position.top 45868 var colliderHeight = initializing ? targetHeight : height 45869 45870 if (offsetTop != null && scrollTop <= offsetTop) return 'top' 45871 if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' 45872 45873 return false 45874 } 45875 45876 Affix.prototype.getPinnedOffset = function () { 45877 if (this.pinnedOffset) return this.pinnedOffset 45878 this.$element.removeClass(Affix.RESET).addClass('affix') 45879 var scrollTop = this.$target.scrollTop() 45880 var position = this.$element.offset() 45881 return (this.pinnedOffset = position.top - scrollTop) 45882 } 45883 45884 Affix.prototype.checkPositionWithEventLoop = function () { 45885 setTimeout($.proxy(this.checkPosition, this), 1) 45886 } 45887 45888 Affix.prototype.checkPosition = function () { 45889 if (!this.$element.is(':visible')) return 45890 45891 var height = this.$element.height() 45892 var offset = this.options.offset 45893 var offsetTop = offset.top 45894 var offsetBottom = offset.bottom 45895 var scrollHeight = Math.max($(document).height(), $(document.body).height()) 45896 45897 if (typeof offset != 'object') offsetBottom = offsetTop = offset 45898 if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) 45899 if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) 45900 45901 var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) 45902 45903 if (this.affixed != affix) { 45904 if (this.unpin != null) this.$element.css('top', '') 45905 45906 var affixType = 'affix' + (affix ? '-' + affix : '') 45907 var e = $.Event(affixType + '.bs.affix') 45908 45909 this.$element.trigger(e) 45910 45911 if (e.isDefaultPrevented()) return 45912 45913 this.affixed = affix 45914 this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null 45915 45916 this.$element 45917 .removeClass(Affix.RESET) 45918 .addClass(affixType) 45919 .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') 45920 } 45921 45922 if (affix == 'bottom') { 45923 this.$element.offset({ 45924 top: scrollHeight - height - offsetBottom 45925 }) 45926 } 45927 } 45928 45929 45930 // AFFIX PLUGIN DEFINITION 45931 // ======================= 45932 45933 function Plugin(option) { 45934 return this.each(function () { 45935 var $this = $(this) 45936 var data = $this.data('bs.affix') 45937 var options = typeof option == 'object' && option 45938 45939 if (!data) $this.data('bs.affix', (data = new Affix(this, options))) 45940 if (typeof option == 'string') data[option]() 45941 }) 45942 } 45943 45944 var old = $.fn.affix 45945 45946 $.fn.affix = Plugin 45947 $.fn.affix.Constructor = Affix 45948 45949 45950 // AFFIX NO CONFLICT 45951 // ================= 45952 45953 $.fn.affix.noConflict = function () { 45954 $.fn.affix = old 45955 return this 45956 } 45957 45958 45959 // AFFIX DATA-API 45960 // ============== 45961 45962 $(window).on('load', function () { 45963 $('[data-spy="affix"]').each(function () { 45964 var $spy = $(this) 45965 var data = $spy.data() 45966 45967 data.offset = data.offset || {} 45968 45969 if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom 45970 if (data.offsetTop != null) data.offset.top = data.offsetTop 45971 45972 Plugin.call($spy, data) 45973 }) 45974 }) 45975 45976}(jQuery);
45977 45978angular.module('NanoPlayer', []) 45979 .service('NanoPlayerService', function () { 45980 var service = {}; 45981 45982 service.playerCss = ` 45983 .nano-player panel { 45984 display: block; 45985 margin: 0px auto; 45986 padding: 5px; 45987 font-size: 40px; 45988 } 45989 .nano-player button { 45990 display: inline-block; 45991 background: none; 45992 border: none; 45993 padding: 3px 0px; 45994 cursor: pointer; 45995 font-size: 40px; 45996 transition: all 0.2s; 45997 } 45998 .nano-player button:hover { 45999 opacity: 0.5; 46000 } 46001 `; 46002 46003 service.panel = ` 46004 <panel> 46005 <button act="play" class="fa fa-play-circle"></button> 46006 <button act="pause" class="fa fa-pause-circle"></button> 46007 <button act="reset" class="fa fa-rotate-left"></button> 46008 </panel> 46009 `; 46010 46011 service.forward = `<button act="forward" class="fa fa-forward ml10"></button>`; 46012 service.rewind = `<button act="back" class="fa fa-backward mr10"></button>`; 46013 46014 service.installStyle = function () { 46015 if (!$('#nano-player-css').length) { 46016 $('<style></style>') 46017 .attr('id', 'nano-player-css') 46018 .attr('type', 'text/css') 46019 .append(service.playerCss) 46020 .appendTo('head'); 46021 } 46022 } 46023 return service; 46024 }) 46025 .directive('mp3Player', ['NanoPlayerService', function (NanoPlayerService) { 46026 return { 46027 restrict: 'E', 46028 template: (c, attr) => `<audio ng-src="${attr.src}" type="audio/mp3"></audio>`, 46029 link: (scope, element, attributes) => { 46030 NanoPlayerService.installStyle(); 46031 $(element).addClass('nano-player').append(NanoPlayerService.panel); 46032 46033 let player = $(element).find('audio').get(0); 46034 let buttons = $(element).find('button'); 46035 46036 buttons.filter('[act="play"]').click((e) => player.play()); 46037 buttons.filter('[act="pause"]').click((e) => player.pause()); 46038 buttons.filter('[act="reset"]').click((e) => player.currentTime = 0); 46039 } 46040 } 46041 }]) 46042 .directive('mp4Player', ['NanoPlayerService', function (NanoPlayerService) { 46043 return { 46044 restrict: 'E', 46045 template: (c, attr) => `<video ng-src="${attr.src}" type="video/mpeg"></video>`, 46046 link: (scope, element, attributes) => { 46047 NanoPlayerService.installStyle(); 46048 $(element).addClass('nano-player').append(NanoPlayerService.panel); 46049 if (attributes.extended) $(element).find('panel').prepend(NanoPlayerService.rewind).append(NanoPlayerService.forward); 46050 if (attributes.poster) $(element).find('video').attr('poster', attributes.poster); 46051 46052 let player = $(element).find('video').get(0); 46053 let buttons = $(element).find('button'); 46054 46055 $(player).click(function () { player.play(); }) 46056 46057 buttons.filter('[act="play"]').click((e) => player.play()); 46058 buttons.filter('[act="pause"]').click((e) => player.pause()); 46059 buttons.filter('[act="reset"]').click((e) => player.currentTime = 0); 46060 buttons.filter('[act="forward"]').click((e) => player.currentTime = player.currentTime + 5); 46061 buttons.filter('[act="back"]').click((e) => player.currentTime = player.currentTime - 5); 46062 } 46063 } 46064 }]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.