2 * jQuery JavaScript Library v2.1.1
8 * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
9 * Released under the MIT license
10 * http://jquery.org/license
12 * Date: 2014-05-01T17:11Z
15 (function( global, factory ) {'use strict';
17 if ( typeof module === "object" && typeof module.exports === "object" ) {
18 // For CommonJS and CommonJS-like environments where a proper window is present,
19 // execute the factory and get jQuery
20 // For environments that do not inherently posses a window with a document
21 // (such as Node.js), expose a jQuery-making factory as module.exports
22 // This accentuates the need for the creation of a real window
23 // e.g. var jQuery = require("jquery")(window);
24 // See ticket #14549 for more info
25 module.exports = global.document ?
26 factory( global, true ) :
29 throw new Error( "jQuery requires a window with a document" );
37 // Pass this if window is not defined yet
38 }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
40 // Can't do this because several apps including ASP.NET trace
41 // the stack via arguments.caller.callee and Firefox dies if
42 // you try to trace through "use strict" call chains. (#13335)
43 // Support: Firefox 18+
48 var slice = arr.slice;
50 var concat = arr.concat;
54 var indexOf = arr.indexOf;
58 var toString = class2type.toString;
60 var hasOwn = class2type.hasOwnProperty;
67 // Use the correct document accordingly with window argument (sandbox)
68 document = window.document,
72 // Define a local copy of jQuery
73 jQuery = function( selector, context ) {
74 // The jQuery object is actually just the init constructor 'enhanced'
75 // Need init if jQuery is called (just allow error to be thrown if not included)
76 return new jQuery.fn.init( selector, context );
79 // Support: Android<4.1
80 // Make sure we trim BOM and NBSP
81 rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
83 // Matches dashed string for camelizing
85 rdashAlpha = /-([\da-z])/gi,
87 // Used by jQuery.camelCase as callback to replace()
88 fcamelCase = function( all, letter ) {
89 return letter.toUpperCase();
92 jQuery.fn = jQuery.prototype = {
93 // The current version of jQuery being used
98 // Start with an empty selector
101 // The default length of a jQuery object is 0
104 toArray: function() {
105 return slice.call( this );
108 // Get the Nth element in the matched element set OR
109 // Get the whole matched element set as a clean array
110 get: function( num ) {
113 // Return just the one element from the set
114 ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
116 // Return all the elements in a clean array
120 // Take an array of elements and push it onto the stack
121 // (returning the new matched element set)
122 pushStack: function( elems ) {
124 // Build a new jQuery matched element set
125 var ret = jQuery.merge( this.constructor(), elems );
127 // Add the old object onto the stack (as a reference)
128 ret.prevObject = this;
129 ret.context = this.context;
131 // Return the newly-formed element set
135 // Execute a callback for every element in the matched set.
136 // (You can seed the arguments with an array of args, but this is
137 // only used internally.)
138 each: function( callback, args ) {
139 return jQuery.each( this, callback, args );
142 map: function( callback ) {
143 return this.pushStack( jQuery.map(this, function( elem, i ) {
144 return callback.call( elem, i, elem );
149 return this.pushStack( slice.apply( this, arguments ) );
157 return this.eq( -1 );
161 var len = this.length,
162 j = +i + ( i < 0 ? len : 0 );
163 return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
167 return this.prevObject || this.constructor(null);
170 // For internal use only.
171 // Behaves like an Array's method, not like a jQuery method.
177 jQuery.extend = jQuery.fn.extend = function() {
178 var options, name, src, copy, copyIsArray, clone,
179 target = arguments[0] || {},
181 length = arguments.length,
184 // Handle a deep copy situation
185 if ( typeof target === "boolean" ) {
188 // skip the boolean and the target
189 target = arguments[ i ] || {};
193 // Handle case when target is a string or something (possible in deep copy)
194 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
198 // extend jQuery itself if only one argument is passed
199 if ( i === length ) {
204 for ( ; i < length; i++ ) {
205 // Only deal with non-null/undefined values
206 if ( (options = arguments[ i ]) != null ) {
207 // Extend the base object
208 for ( name in options ) {
209 src = target[ name ];
210 copy = options[ name ];
212 // Prevent never-ending loop
213 if ( target === copy ) {
217 // Recurse if we're merging plain objects or arrays
218 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
221 clone = src && jQuery.isArray(src) ? src : [];
224 clone = src && jQuery.isPlainObject(src) ? src : {};
227 // Never move original objects, clone them
228 target[ name ] = jQuery.extend( deep, clone, copy );
230 // Don't bring in undefined values
231 } else if ( copy !== undefined ) {
232 target[ name ] = copy;
238 // Return the modified object
243 // Unique for each copy of jQuery on the page
244 expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
246 // Assume jQuery is ready without the ready module
249 error: function( msg ) {
250 throw new Error( msg );
255 // See test/unit/core.js for details concerning isFunction.
256 // Since version 1.3, DOM methods and functions like alert
257 // aren't supported. They return false on IE (#2968).
258 isFunction: function( obj ) {
259 return jQuery.type(obj) === "function";
262 isArray: Array.isArray,
264 isWindow: function( obj ) {
265 return obj != null && obj === obj.window;
268 isNumeric: function( obj ) {
269 // parseFloat NaNs numeric-cast false positives (null|true|false|"")
270 // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
271 // subtraction forces infinities to NaN
272 return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
275 isPlainObject: function( obj ) {
276 // Not plain objects:
277 // - Any object or value whose internal [[Class]] property is not "[object Object]"
280 if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
284 if ( obj.constructor &&
285 !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
289 // If the function hasn't returned already, we're confident that
290 // |obj| is a plain object, created by {} or constructed with new Object
294 isEmptyObject: function( obj ) {
296 for ( name in obj ) {
302 type: function( obj ) {
306 // Support: Android < 4.0, iOS < 6 (functionish RegExp)
307 return typeof obj === "object" || typeof obj === "function" ?
308 class2type[ toString.call(obj) ] || "object" :
312 // Evaluates a script in a global context
313 globalEval: function( code ) {
317 code = jQuery.trim( code );
320 // If the code includes a valid, prologue position
321 // strict mode pragma, execute code by injecting a
322 // script tag into the document.
323 if ( code.indexOf("use strict") === 1 ) {
324 script = document.createElement("script");
326 document.head.appendChild( script ).parentNode.removeChild( script );
328 // Otherwise, avoid the DOM node creation, insertion
329 // and removal by using an indirect global eval
335 // Convert dashed to camelCase; used by the css and data modules
336 // Microsoft forgot to hump their vendor prefix (#9572)
337 camelCase: function( string ) {
338 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
341 nodeName: function( elem, name ) {
342 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
345 // args is for internal usage only
346 each: function( obj, callback, args ) {
350 isArray = isArraylike( obj );
354 for ( ; i < length; i++ ) {
355 value = callback.apply( obj[ i ], args );
357 if ( value === false ) {
363 value = callback.apply( obj[ i ], args );
365 if ( value === false ) {
371 // A special, fast, case for the most common use of each
374 for ( ; i < length; i++ ) {
375 value = callback.call( obj[ i ], i, obj[ i ] );
377 if ( value === false ) {
383 value = callback.call( obj[ i ], i, obj[ i ] );
385 if ( value === false ) {
395 // Support: Android<4.1
396 trim: function( text ) {
397 return text == null ?
399 ( text + "" ).replace( rtrim, "" );
402 // results is for internal usage only
403 makeArray: function( arr, results ) {
404 var ret = results || [];
407 if ( isArraylike( Object(arr) ) ) {
409 typeof arr === "string" ?
413 push.call( ret, arr );
420 inArray: function( elem, arr, i ) {
421 return arr == null ? -1 : indexOf.call( arr, elem, i );
424 merge: function( first, second ) {
425 var len = +second.length,
429 for ( ; j < len; j++ ) {
430 first[ i++ ] = second[ j ];
438 grep: function( elems, callback, invert ) {
442 length = elems.length,
443 callbackExpect = !invert;
445 // Go through the array, only saving the items
446 // that pass the validator function
447 for ( ; i < length; i++ ) {
448 callbackInverse = !callback( elems[ i ], i );
449 if ( callbackInverse !== callbackExpect ) {
450 matches.push( elems[ i ] );
457 // arg is for internal usage only
458 map: function( elems, callback, arg ) {
461 length = elems.length,
462 isArray = isArraylike( elems ),
465 // Go through the array, translating each of the items to their new values
467 for ( ; i < length; i++ ) {
468 value = callback( elems[ i ], i, arg );
470 if ( value != null ) {
475 // Go through every key on the object,
478 value = callback( elems[ i ], i, arg );
480 if ( value != null ) {
486 // Flatten any nested arrays
487 return concat.apply( [], ret );
490 // A global GUID counter for objects
493 // Bind a function to a context, optionally partially applying any
495 proxy: function( fn, context ) {
496 var tmp, args, proxy;
498 if ( typeof context === "string" ) {
504 // Quick check to determine if target is callable, in the spec
505 // this throws a TypeError, but we will just return undefined.
506 if ( !jQuery.isFunction( fn ) ) {
511 args = slice.call( arguments, 2 );
513 return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
516 // Set the guid of unique handler to the same of original handler, so it can be removed
517 proxy.guid = fn.guid = fn.guid || jQuery.guid++;
524 // jQuery.support is not used in Core but other projects attach their
525 // properties to it so it needs to exist.
529 // Populate the class2type map
530 jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
531 class2type[ "[object " + name + "]" ] = name.toLowerCase();
534 function isArraylike( obj ) {
535 var length = obj.length,
536 type = jQuery.type( obj );
538 if ( type === "function" || jQuery.isWindow( obj ) ) {
542 if ( obj.nodeType === 1 && length ) {
546 return type === "array" || length === 0 ||
547 typeof length === "number" && length > 0 && ( length - 1 ) in obj;
551 * Sizzle CSS Selector Engine v1.10.19
552 * http://sizzlejs.com/
554 * Copyright 2013 jQuery Foundation, Inc. and other contributors
555 * Released under the MIT license
556 * http://jquery.org/license
560 (function( window ) {
574 // Local document vars
584 // Instance-specific data
585 expando = "sizzle" + -(new Date()),
586 preferredDoc = window.document,
589 classCache = createCache(),
590 tokenCache = createCache(),
591 compilerCache = createCache(),
592 sortOrder = function( a, b ) {
599 // General-purpose constants
600 strundefined = typeof undefined,
601 MAX_NEGATIVE = 1 << 31,
604 hasOwn = ({}).hasOwnProperty,
607 push_native = arr.push,
610 // Use a stripped-down indexOf if we can't use a native one
611 indexOf = arr.indexOf || function( elem ) {
614 for ( ; i < len; i++ ) {
615 if ( this[i] === elem ) {
622 booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
624 // Regular expressions
626 // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
627 whitespace = "[\\x20\\t\\r\\n\\f]",
628 // http://www.w3.org/TR/css3-syntax/#characters
629 characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
631 // Loosely modeled on CSS identifier characters
632 // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
633 // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
634 identifier = characterEncoding.replace( "w", "w#" ),
636 // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
637 attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
638 // Operator (capture 2)
639 "*([*^$|!~]?=)" + whitespace +
640 // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
641 "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
644 pseudos = ":(" + characterEncoding + ")(?:\\((" +
645 // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
646 // 1. quoted (capture 3; capture 4 or capture 5)
647 "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
648 // 2. simple (capture 6)
649 "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
650 // 3. anything else (capture 2)
654 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
655 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
657 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
658 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
660 rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
662 rpseudo = new RegExp( pseudos ),
663 ridentifier = new RegExp( "^" + identifier + "$" ),
666 "ID": new RegExp( "^#(" + characterEncoding + ")" ),
667 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
668 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
669 "ATTR": new RegExp( "^" + attributes ),
670 "PSEUDO": new RegExp( "^" + pseudos ),
671 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
672 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
673 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
674 "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
675 // For use in libraries implementing .is()
676 // We use this for POS matching in `select`
677 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
678 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
681 rinputs = /^(?:input|select|textarea|button)$/i,
684 rnative = /^[^{]+\{\s*\[native \w/,
686 // Easily-parseable/retrievable ID or TAG or CLASS selectors
687 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
692 // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
693 runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
694 funescape = function( _, escaped, escapedWhitespace ) {
695 var high = "0x" + escaped - 0x10000;
696 // NaN means non-codepoint
697 // Support: Firefox<24
698 // Workaround erroneous numeric interpretation of +"0x"
699 return high !== high || escapedWhitespace ?
703 String.fromCharCode( high + 0x10000 ) :
704 // Supplemental Plane codepoint (surrogate pair)
705 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
708 // Optimize for push.apply( _, NodeList )
711 (arr = slice.call( preferredDoc.childNodes )),
712 preferredDoc.childNodes
714 // Support: Android<4.0
715 // Detect silently failing push.apply
716 arr[ preferredDoc.childNodes.length ].nodeType;
718 push = { apply: arr.length ?
720 // Leverage slice if possible
721 function( target, els ) {
722 push_native.apply( target, slice.call(els) );
726 // Otherwise append directly
727 function( target, els ) {
728 var j = target.length,
730 // Can't trust NodeList.length
731 while ( (target[j++] = els[i++]) ) {}
732 target.length = j - 1;
737 function Sizzle( selector, context, results, seed ) {
738 var match, elem, m, nodeType,
740 i, groups, old, nid, newContext, newSelector;
742 if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
743 setDocument( context );
746 context = context || document;
747 results = results || [];
749 if ( !selector || typeof selector !== "string" ) {
753 if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
757 if ( documentIsHTML && !seed ) {
760 if ( (match = rquickExpr.exec( selector )) ) {
761 // Speed-up: Sizzle("#ID")
762 if ( (m = match[1]) ) {
763 if ( nodeType === 9 ) {
764 elem = context.getElementById( m );
765 // Check parentNode to catch when Blackberry 4.6 returns
766 // nodes that are no longer in the document (jQuery #6963)
767 if ( elem && elem.parentNode ) {
768 // Handle the case where IE, Opera, and Webkit return items
769 // by name instead of ID
770 if ( elem.id === m ) {
771 results.push( elem );
778 // Context is not a document
779 if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
780 contains( context, elem ) && elem.id === m ) {
781 results.push( elem );
786 // Speed-up: Sizzle("TAG")
787 } else if ( match[2] ) {
788 push.apply( results, context.getElementsByTagName( selector ) );
791 // Speed-up: Sizzle(".CLASS")
792 } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
793 push.apply( results, context.getElementsByClassName( m ) );
799 if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
801 newContext = context;
802 newSelector = nodeType === 9 && selector;
804 // qSA works strangely on Element-rooted queries
805 // We can work around this by specifying an extra ID on the root
806 // and working up from there (Thanks to Andrew Dupont for the technique)
807 // IE 8 doesn't work on object elements
808 if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
809 groups = tokenize( selector );
811 if ( (old = context.getAttribute("id")) ) {
812 nid = old.replace( rescape, "\\$&" );
814 context.setAttribute( "id", nid );
816 nid = "[id='" + nid + "'] ";
820 groups[i] = nid + toSelector( groups[i] );
822 newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
823 newSelector = groups.join(",");
829 newContext.querySelectorAll( newSelector )
835 context.removeAttribute("id");
843 return select( selector.replace( rtrim, "$1" ), context, results, seed );
847 * Create key-value caches of limited size
848 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
849 * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
850 * deleting the oldest entry
852 function createCache() {
855 function cache( key, value ) {
856 // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
857 if ( keys.push( key + " " ) > Expr.cacheLength ) {
858 // Only keep the most recent entries
859 delete cache[ keys.shift() ];
861 return (cache[ key + " " ] = value);
867 * Mark a function for special use by Sizzle
868 * @param {Function} fn The function to mark
870 function markFunction( fn ) {
871 fn[ expando ] = true;
876 * Support testing using an element
877 * @param {Function} fn Passed the created div and expects a boolean result
879 function assert( fn ) {
880 var div = document.createElement("div");
887 // Remove from its parent by default
888 if ( div.parentNode ) {
889 div.parentNode.removeChild( div );
891 // release memory in IE
897 * Adds the same handler for all of the specified attrs
898 * @param {String} attrs Pipe-separated list of attributes
899 * @param {Function} handler The method that will be applied
901 function addHandle( attrs, handler ) {
902 var arr = attrs.split("|"),
906 Expr.attrHandle[ arr[i] ] = handler;
911 * Checks document order of two siblings
914 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
916 function siblingCheck( a, b ) {
918 diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
919 ( ~b.sourceIndex || MAX_NEGATIVE ) -
920 ( ~a.sourceIndex || MAX_NEGATIVE );
922 // Use IE sourceIndex if available on both nodes
927 // Check if b follows a
929 while ( (cur = cur.nextSibling) ) {
940 * Returns a function to use in pseudos for input types
941 * @param {String} type
943 function createInputPseudo( type ) {
944 return function( elem ) {
945 var name = elem.nodeName.toLowerCase();
946 return name === "input" && elem.type === type;
951 * Returns a function to use in pseudos for buttons
952 * @param {String} type
954 function createButtonPseudo( type ) {
955 return function( elem ) {
956 var name = elem.nodeName.toLowerCase();
957 return (name === "input" || name === "button") && elem.type === type;
962 * Returns a function to use in pseudos for positionals
963 * @param {Function} fn
965 function createPositionalPseudo( fn ) {
966 return markFunction(function( argument ) {
967 argument = +argument;
968 return markFunction(function( seed, matches ) {
970 matchIndexes = fn( [], seed.length, argument ),
971 i = matchIndexes.length;
973 // Match elements found at the specified indexes
975 if ( seed[ (j = matchIndexes[i]) ] ) {
976 seed[j] = !(matches[j] = seed[j]);
984 * Checks a node for validity as a Sizzle context
985 * @param {Element|Object=} context
986 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
988 function testContext( context ) {
989 return context && typeof context.getElementsByTagName !== strundefined && context;
992 // Expose support vars for convenience
993 support = Sizzle.support = {};
997 * @param {Element|Object} elem An element or a document
998 * @returns {Boolean} True iff elem is a non-HTML XML node
1000 isXML = Sizzle.isXML = function( elem ) {
1001 // documentElement is verified for cases where it doesn't yet exist
1002 // (such as loading iframes in IE - #4833)
1003 var documentElement = elem && (elem.ownerDocument || elem).documentElement;
1004 return documentElement ? documentElement.nodeName !== "HTML" : false;
1008 * Sets document-related variables once based on the current document
1009 * @param {Element|Object} [doc] An element or document object to use to set the document
1010 * @returns {Object} Returns the current document
1012 setDocument = Sizzle.setDocument = function( node ) {
1014 doc = node ? node.ownerDocument || node : preferredDoc,
1015 parent = doc.defaultView;
1017 // If no document and documentElement is available, return
1018 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
1024 docElem = doc.documentElement;
1027 documentIsHTML = !isXML( doc );
1030 // If iframe document is assigned to "document" variable and if iframe has been reloaded,
1031 // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
1032 // IE6-8 do not support the defaultView property so parent will be undefined
1033 if ( parent && parent !== parent.top ) {
1034 // IE11 does not have attachEvent, so all must suffer
1035 if ( parent.addEventListener ) {
1036 parent.addEventListener( "unload", function() {
1039 } else if ( parent.attachEvent ) {
1040 parent.attachEvent( "onunload", function() {
1047 ---------------------------------------------------------------------- */
1050 // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
1051 support.attributes = assert(function( div ) {
1052 div.className = "i";
1053 return !div.getAttribute("className");
1057 ---------------------------------------------------------------------- */
1059 // Check if getElementsByTagName("*") returns only elements
1060 support.getElementsByTagName = assert(function( div ) {
1061 div.appendChild( doc.createComment("") );
1062 return !div.getElementsByTagName("*").length;
1065 // Check if getElementsByClassName can be trusted
1066 support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
1067 div.innerHTML = "<div class='a'></div><div class='a i'></div>";
1069 // Support: Safari<4
1070 // Catch class over-caching
1071 div.firstChild.className = "i";
1072 // Support: Opera<10
1073 // Catch gEBCN failure to find non-leading classes
1074 return div.getElementsByClassName("i").length === 2;
1078 // Check if getElementById returns elements by name
1079 // The broken getElementById methods don't pick up programatically-set names,
1080 // so use a roundabout getElementsByName test
1081 support.getById = assert(function( div ) {
1082 docElem.appendChild( div ).id = expando;
1083 return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
1086 // ID find and filter
1087 if ( support.getById ) {
1088 Expr.find["ID"] = function( id, context ) {
1089 if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
1090 var m = context.getElementById( id );
1091 // Check parentNode to catch when Blackberry 4.6 returns
1092 // nodes that are no longer in the document #6963
1093 return m && m.parentNode ? [ m ] : [];
1096 Expr.filter["ID"] = function( id ) {
1097 var attrId = id.replace( runescape, funescape );
1098 return function( elem ) {
1099 return elem.getAttribute("id") === attrId;
1104 // getElementById is not reliable as a find shortcut
1105 delete Expr.find["ID"];
1107 Expr.filter["ID"] = function( id ) {
1108 var attrId = id.replace( runescape, funescape );
1109 return function( elem ) {
1110 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
1111 return node && node.value === attrId;
1117 Expr.find["TAG"] = support.getElementsByTagName ?
1118 function( tag, context ) {
1119 if ( typeof context.getElementsByTagName !== strundefined ) {
1120 return context.getElementsByTagName( tag );
1123 function( tag, context ) {
1127 results = context.getElementsByTagName( tag );
1129 // Filter out possible comments
1130 if ( tag === "*" ) {
1131 while ( (elem = results[i++]) ) {
1132 if ( elem.nodeType === 1 ) {
1143 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
1144 if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
1145 return context.getElementsByClassName( className );
1149 /* QSA/matchesSelector
1150 ---------------------------------------------------------------------- */
1152 // QSA and matchesSelector support
1154 // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
1157 // qSa(:focus) reports false when true (Chrome 21)
1158 // We allow this because of a bug in IE8/9 that throws an error
1159 // whenever `document.activeElement` is accessed on an iframe
1160 // So, we allow :focus to pass through QSA all the time to avoid the IE error
1161 // See http://bugs.jquery.com/ticket/13378
1164 if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
1166 // Regex strategy adopted from Diego Perini
1167 assert(function( div ) {
1168 // Select is set to empty string on purpose
1169 // This is to test IE's treatment of not explicitly
1170 // setting a boolean content attribute,
1171 // since its presence should be enough
1172 // http://bugs.jquery.com/ticket/12359
1173 div.innerHTML = "<select msallowclip=''><option selected=''></option></select>";
1175 // Support: IE8, Opera 11-12.16
1176 // Nothing should be selected when empty strings follow ^= or $= or *=
1177 // The test attribute must be unknown in Opera but "safe" for WinRT
1178 // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
1179 if ( div.querySelectorAll("[msallowclip^='']").length ) {
1180 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
1184 // Boolean attributes and "value" are not treated correctly
1185 if ( !div.querySelectorAll("[selected]").length ) {
1186 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
1189 // Webkit/Opera - :checked should return selected option elements
1190 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1191 // IE8 throws error here and will not see later tests
1192 if ( !div.querySelectorAll(":checked").length ) {
1193 rbuggyQSA.push(":checked");
1197 assert(function( div ) {
1198 // Support: Windows 8 Native Apps
1199 // The type and name attributes are restricted during .innerHTML assignment
1200 var input = doc.createElement("input");
1201 input.setAttribute( "type", "hidden" );
1202 div.appendChild( input ).setAttribute( "name", "D" );
1205 // Enforce case-sensitivity of name attribute
1206 if ( div.querySelectorAll("[name=d]").length ) {
1207 rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
1210 // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
1211 // IE8 throws error here and will not see later tests
1212 if ( !div.querySelectorAll(":enabled").length ) {
1213 rbuggyQSA.push( ":enabled", ":disabled" );
1216 // Opera 10-11 does not throw on post-comma invalid pseudos
1217 div.querySelectorAll("*,:x");
1218 rbuggyQSA.push(",.*:");
1222 if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
1223 docElem.webkitMatchesSelector ||
1224 docElem.mozMatchesSelector ||
1225 docElem.oMatchesSelector ||
1226 docElem.msMatchesSelector) )) ) {
1228 assert(function( div ) {
1229 // Check to see if it's possible to do matchesSelector
1230 // on a disconnected node (IE 9)
1231 support.disconnectedMatch = matches.call( div, "div" );
1233 // This should fail with an exception
1234 // Gecko does not error, returns false instead
1235 matches.call( div, "[s!='']:x" );
1236 rbuggyMatches.push( "!=", pseudos );
1240 rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
1241 rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
1244 ---------------------------------------------------------------------- */
1245 hasCompare = rnative.test( docElem.compareDocumentPosition );
1247 // Element contains another
1248 // Purposefully does not implement inclusive descendent
1249 // As in, an element does not contain itself
1250 contains = hasCompare || rnative.test( docElem.contains ) ?
1252 var adown = a.nodeType === 9 ? a.documentElement : a,
1253 bup = b && b.parentNode;
1254 return a === bup || !!( bup && bup.nodeType === 1 && (
1256 adown.contains( bup ) :
1257 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
1262 while ( (b = b.parentNode) ) {
1272 ---------------------------------------------------------------------- */
1274 // Document order sorting
1275 sortOrder = hasCompare ?
1278 // Flag for duplicate removal
1280 hasDuplicate = true;
1284 // Sort on method existence if only one input has compareDocumentPosition
1285 var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
1290 // Calculate position if both inputs belong to the same document
1291 compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
1292 a.compareDocumentPosition( b ) :
1294 // Otherwise we know they are disconnected
1297 // Disconnected nodes
1299 (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
1301 // Choose the first element that is related to our preferred document
1302 if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
1305 if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
1309 // Maintain original order
1311 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1315 return compare & 4 ? -1 : 1;
1318 // Exit early if the nodes are identical
1320 hasDuplicate = true;
1331 // Parentless nodes are either documents or disconnected
1332 if ( !aup || !bup ) {
1333 return a === doc ? -1 :
1338 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1341 // If the nodes are siblings, we can do a quick check
1342 } else if ( aup === bup ) {
1343 return siblingCheck( a, b );
1346 // Otherwise we need full lists of their ancestors for comparison
1348 while ( (cur = cur.parentNode) ) {
1352 while ( (cur = cur.parentNode) ) {
1356 // Walk down the tree looking for a discrepancy
1357 while ( ap[i] === bp[i] ) {
1362 // Do a sibling check if the nodes have a common ancestor
1363 siblingCheck( ap[i], bp[i] ) :
1365 // Otherwise nodes in our document sort first
1366 ap[i] === preferredDoc ? -1 :
1367 bp[i] === preferredDoc ? 1 :
1374 Sizzle.matches = function( expr, elements ) {
1375 return Sizzle( expr, null, null, elements );
1378 Sizzle.matchesSelector = function( elem, expr ) {
1379 // Set document vars if needed
1380 if ( ( elem.ownerDocument || elem ) !== document ) {
1381 setDocument( elem );
1384 // Make sure that attribute selectors are quoted
1385 expr = expr.replace( rattributeQuotes, "='$1']" );
1387 if ( support.matchesSelector && documentIsHTML &&
1388 ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
1389 ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
1392 var ret = matches.call( elem, expr );
1394 // IE 9's matchesSelector returns false on disconnected nodes
1395 if ( ret || support.disconnectedMatch ||
1396 // As well, disconnected nodes are said to be in a document
1398 elem.document && elem.document.nodeType !== 11 ) {
1404 return Sizzle( expr, document, null, [ elem ] ).length > 0;
1407 Sizzle.contains = function( context, elem ) {
1408 // Set document vars if needed
1409 if ( ( context.ownerDocument || context ) !== document ) {
1410 setDocument( context );
1412 return contains( context, elem );
1415 Sizzle.attr = function( elem, name ) {
1416 // Set document vars if needed
1417 if ( ( elem.ownerDocument || elem ) !== document ) {
1418 setDocument( elem );
1421 var fn = Expr.attrHandle[ name.toLowerCase() ],
1422 // Don't get fooled by Object.prototype properties (jQuery #13807)
1423 val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
1424 fn( elem, name, !documentIsHTML ) :
1427 return val !== undefined ?
1429 support.attributes || !documentIsHTML ?
1430 elem.getAttribute( name ) :
1431 (val = elem.getAttributeNode(name)) && val.specified ?
1436 Sizzle.error = function( msg ) {
1437 throw new Error( "Syntax error, unrecognized expression: " + msg );
1441 * Document sorting and removing duplicates
1442 * @param {ArrayLike} results
1444 Sizzle.uniqueSort = function( results ) {
1450 // Unless we *know* we can detect duplicates, assume their presence
1451 hasDuplicate = !support.detectDuplicates;
1452 sortInput = !support.sortStable && results.slice( 0 );
1453 results.sort( sortOrder );
1455 if ( hasDuplicate ) {
1456 while ( (elem = results[i++]) ) {
1457 if ( elem === results[ i ] ) {
1458 j = duplicates.push( i );
1462 results.splice( duplicates[ j ], 1 );
1466 // Clear input after sorting to release objects
1467 // See https://github.com/jquery/sizzle/pull/225
1474 * Utility function for retrieving the text value of an array of DOM nodes
1475 * @param {Array|Element} elem
1477 getText = Sizzle.getText = function( elem ) {
1481 nodeType = elem.nodeType;
1484 // If no nodeType, this is expected to be an array
1485 while ( (node = elem[i++]) ) {
1486 // Do not traverse comment nodes
1487 ret += getText( node );
1489 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
1490 // Use textContent for elements
1491 // innerText usage removed for consistency of new lines (jQuery #11153)
1492 if ( typeof elem.textContent === "string" ) {
1493 return elem.textContent;
1495 // Traverse its children
1496 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1497 ret += getText( elem );
1500 } else if ( nodeType === 3 || nodeType === 4 ) {
1501 return elem.nodeValue;
1503 // Do not include comment or processing instruction nodes
1508 Expr = Sizzle.selectors = {
1510 // Can be adjusted by the user
1513 createPseudo: markFunction,
1522 ">": { dir: "parentNode", first: true },
1523 " ": { dir: "parentNode" },
1524 "+": { dir: "previousSibling", first: true },
1525 "~": { dir: "previousSibling" }
1529 "ATTR": function( match ) {
1530 match[1] = match[1].replace( runescape, funescape );
1532 // Move the given value to match[3] whether quoted or unquoted
1533 match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
1535 if ( match[2] === "~=" ) {
1536 match[3] = " " + match[3] + " ";
1539 return match.slice( 0, 4 );
1542 "CHILD": function( match ) {
1543 /* matches from matchExpr["CHILD"]
1544 1 type (only|nth|...)
1545 2 what (child|of-type)
1546 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
1547 4 xn-component of xn+y argument ([+-]?\d*n|)
1548 5 sign of xn-component
1550 7 sign of y-component
1553 match[1] = match[1].toLowerCase();
1555 if ( match[1].slice( 0, 3 ) === "nth" ) {
1556 // nth-* requires argument
1558 Sizzle.error( match[0] );
1561 // numeric x and y parameters for Expr.filter.CHILD
1562 // remember that false/true cast respectively to 0/1
1563 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
1564 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
1566 // other types prohibit arguments
1567 } else if ( match[3] ) {
1568 Sizzle.error( match[0] );
1574 "PSEUDO": function( match ) {
1576 unquoted = !match[6] && match[2];
1578 if ( matchExpr["CHILD"].test( match[0] ) ) {
1582 // Accept quoted arguments as-is
1584 match[2] = match[4] || match[5] || "";
1586 // Strip excess characters from unquoted arguments
1587 } else if ( unquoted && rpseudo.test( unquoted ) &&
1588 // Get excess from tokenize (recursively)
1589 (excess = tokenize( unquoted, true )) &&
1590 // advance to the next closing parenthesis
1591 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
1593 // excess is a negative index
1594 match[0] = match[0].slice( 0, excess );
1595 match[2] = unquoted.slice( 0, excess );
1598 // Return only captures needed by the pseudo filter method (type and argument)
1599 return match.slice( 0, 3 );
1605 "TAG": function( nodeNameSelector ) {
1606 var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
1607 return nodeNameSelector === "*" ?
1608 function() { return true; } :
1610 return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
1614 "CLASS": function( className ) {
1615 var pattern = classCache[ className + " " ];
1618 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
1619 classCache( className, function( elem ) {
1620 return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
1624 "ATTR": function( name, operator, check ) {
1625 return function( elem ) {
1626 var result = Sizzle.attr( elem, name );
1628 if ( result == null ) {
1629 return operator === "!=";
1637 return operator === "=" ? result === check :
1638 operator === "!=" ? result !== check :
1639 operator === "^=" ? check && result.indexOf( check ) === 0 :
1640 operator === "*=" ? check && result.indexOf( check ) > -1 :
1641 operator === "$=" ? check && result.slice( -check.length ) === check :
1642 operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
1643 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
1648 "CHILD": function( type, what, argument, first, last ) {
1649 var simple = type.slice( 0, 3 ) !== "nth",
1650 forward = type.slice( -4 ) !== "last",
1651 ofType = what === "of-type";
1653 return first === 1 && last === 0 ?
1655 // Shortcut for :nth-*(n)
1657 return !!elem.parentNode;
1660 function( elem, context, xml ) {
1661 var cache, outerCache, node, diff, nodeIndex, start,
1662 dir = simple !== forward ? "nextSibling" : "previousSibling",
1663 parent = elem.parentNode,
1664 name = ofType && elem.nodeName.toLowerCase(),
1665 useCache = !xml && !ofType;
1669 // :(first|last|only)-(child|of-type)
1673 while ( (node = node[ dir ]) ) {
1674 if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
1678 // Reverse direction for :only-* (if we haven't yet done so)
1679 start = dir = type === "only" && !start && "nextSibling";
1684 start = [ forward ? parent.firstChild : parent.lastChild ];
1686 // non-xml :nth-child(...) stores cache data on `parent`
1687 if ( forward && useCache ) {
1688 // Seek `elem` from a previously-cached index
1689 outerCache = parent[ expando ] || (parent[ expando ] = {});
1690 cache = outerCache[ type ] || [];
1691 nodeIndex = cache[0] === dirruns && cache[1];
1692 diff = cache[0] === dirruns && cache[2];
1693 node = nodeIndex && parent.childNodes[ nodeIndex ];
1695 while ( (node = ++nodeIndex && node && node[ dir ] ||
1697 // Fallback to seeking `elem` from the start
1698 (diff = nodeIndex = 0) || start.pop()) ) {
1700 // When found, cache indexes on `parent` and break
1701 if ( node.nodeType === 1 && ++diff && node === elem ) {
1702 outerCache[ type ] = [ dirruns, nodeIndex, diff ];
1707 // Use previously-cached element index if available
1708 } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
1711 // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
1713 // Use the same loop as above to seek `elem` from the start
1714 while ( (node = ++nodeIndex && node && node[ dir ] ||
1715 (diff = nodeIndex = 0) || start.pop()) ) {
1717 if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
1718 // Cache the index of each encountered element
1720 (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
1723 if ( node === elem ) {
1730 // Incorporate the offset, then check against cycle size
1732 return diff === first || ( diff % first === 0 && diff / first >= 0 );
1737 "PSEUDO": function( pseudo, argument ) {
1738 // pseudo-class names are case-insensitive
1739 // http://www.w3.org/TR/selectors/#pseudo-classes
1740 // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
1741 // Remember that setFilters inherits from pseudos
1743 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
1744 Sizzle.error( "unsupported pseudo: " + pseudo );
1746 // The user may use createPseudo to indicate that
1747 // arguments are needed to create the filter function
1748 // just as Sizzle does
1749 if ( fn[ expando ] ) {
1750 return fn( argument );
1753 // But maintain support for old signatures
1754 if ( fn.length > 1 ) {
1755 args = [ pseudo, pseudo, "", argument ];
1756 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
1757 markFunction(function( seed, matches ) {
1759 matched = fn( seed, argument ),
1762 idx = indexOf.call( seed, matched[i] );
1763 seed[ idx ] = !( matches[ idx ] = matched[i] );
1767 return fn( elem, 0, args );
1776 // Potentially complex pseudos
1777 "not": markFunction(function( selector ) {
1778 // Trim the selector passed to compile
1779 // to avoid treating leading and trailing
1780 // spaces as combinators
1783 matcher = compile( selector.replace( rtrim, "$1" ) );
1785 return matcher[ expando ] ?
1786 markFunction(function( seed, matches, context, xml ) {
1788 unmatched = matcher( seed, null, xml, [] ),
1791 // Match elements unmatched by `matcher`
1793 if ( (elem = unmatched[i]) ) {
1794 seed[i] = !(matches[i] = elem);
1798 function( elem, context, xml ) {
1800 matcher( input, null, xml, results );
1801 return !results.pop();
1805 "has": markFunction(function( selector ) {
1806 return function( elem ) {
1807 return Sizzle( selector, elem ).length > 0;
1811 "contains": markFunction(function( text ) {
1812 return function( elem ) {
1813 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
1817 // "Whether an element is represented by a :lang() selector
1818 // is based solely on the element's language value
1819 // being equal to the identifier C,
1820 // or beginning with the identifier C immediately followed by "-".
1821 // The matching of C against the element's language value is performed case-insensitively.
1822 // The identifier C does not have to be a valid language name."
1823 // http://www.w3.org/TR/selectors/#lang-pseudo
1824 "lang": markFunction( function( lang ) {
1825 // lang value must be a valid identifier
1826 if ( !ridentifier.test(lang || "") ) {
1827 Sizzle.error( "unsupported lang: " + lang );
1829 lang = lang.replace( runescape, funescape ).toLowerCase();
1830 return function( elem ) {
1833 if ( (elemLang = documentIsHTML ?
1835 elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
1837 elemLang = elemLang.toLowerCase();
1838 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
1840 } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
1846 "target": function( elem ) {
1847 var hash = window.location && window.location.hash;
1848 return hash && hash.slice( 1 ) === elem.id;
1851 "root": function( elem ) {
1852 return elem === docElem;
1855 "focus": function( elem ) {
1856 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
1859 // Boolean properties
1860 "enabled": function( elem ) {
1861 return elem.disabled === false;
1864 "disabled": function( elem ) {
1865 return elem.disabled === true;
1868 "checked": function( elem ) {
1869 // In CSS3, :checked should return both checked and selected elements
1870 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1871 var nodeName = elem.nodeName.toLowerCase();
1872 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
1875 "selected": function( elem ) {
1876 // Accessing this property makes selected-by-default
1877 // options in Safari work properly
1878 if ( elem.parentNode ) {
1879 elem.parentNode.selectedIndex;
1882 return elem.selected === true;
1886 "empty": function( elem ) {
1887 // http://www.w3.org/TR/selectors/#empty-pseudo
1888 // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
1889 // but not by others (comment: 8; processing instruction: 7; etc.)
1890 // nodeType < 6 works because attributes (2) do not appear as children
1891 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1892 if ( elem.nodeType < 6 ) {
1899 "parent": function( elem ) {
1900 return !Expr.pseudos["empty"]( elem );
1903 // Element/input types
1904 "header": function( elem ) {
1905 return rheader.test( elem.nodeName );
1908 "input": function( elem ) {
1909 return rinputs.test( elem.nodeName );
1912 "button": function( elem ) {
1913 var name = elem.nodeName.toLowerCase();
1914 return name === "input" && elem.type === "button" || name === "button";
1917 "text": function( elem ) {
1919 return elem.nodeName.toLowerCase() === "input" &&
1920 elem.type === "text" &&
1923 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
1924 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
1927 // Position-in-collection
1928 "first": createPositionalPseudo(function() {
1932 "last": createPositionalPseudo(function( matchIndexes, length ) {
1933 return [ length - 1 ];
1936 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
1937 return [ argument < 0 ? argument + length : argument ];
1940 "even": createPositionalPseudo(function( matchIndexes, length ) {
1942 for ( ; i < length; i += 2 ) {
1943 matchIndexes.push( i );
1945 return matchIndexes;
1948 "odd": createPositionalPseudo(function( matchIndexes, length ) {
1950 for ( ; i < length; i += 2 ) {
1951 matchIndexes.push( i );
1953 return matchIndexes;
1956 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
1957 var i = argument < 0 ? argument + length : argument;
1958 for ( ; --i >= 0; ) {
1959 matchIndexes.push( i );
1961 return matchIndexes;
1964 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
1965 var i = argument < 0 ? argument + length : argument;
1966 for ( ; ++i < length; ) {
1967 matchIndexes.push( i );
1969 return matchIndexes;
1974 Expr.pseudos["nth"] = Expr.pseudos["eq"];
1976 // Add button/input type pseudos
1977 for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
1978 Expr.pseudos[ i ] = createInputPseudo( i );
1980 for ( i in { submit: true, reset: true } ) {
1981 Expr.pseudos[ i ] = createButtonPseudo( i );
1984 // Easy API for creating new setFilters
1985 function setFilters() {}
1986 setFilters.prototype = Expr.filters = Expr.pseudos;
1987 Expr.setFilters = new setFilters();
1989 tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
1990 var matched, match, tokens, type,
1991 soFar, groups, preFilters,
1992 cached = tokenCache[ selector + " " ];
1995 return parseOnly ? 0 : cached.slice( 0 );
2000 preFilters = Expr.preFilter;
2004 // Comma and first run
2005 if ( !matched || (match = rcomma.exec( soFar )) ) {
2007 // Don't consume trailing commas as valid
2008 soFar = soFar.slice( match[0].length ) || soFar;
2010 groups.push( (tokens = []) );
2016 if ( (match = rcombinators.exec( soFar )) ) {
2017 matched = match.shift();
2020 // Cast descendant combinators to space
2021 type: match[0].replace( rtrim, " " )
2023 soFar = soFar.slice( matched.length );
2027 for ( type in Expr.filter ) {
2028 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
2029 (match = preFilters[ type ]( match ))) ) {
2030 matched = match.shift();
2036 soFar = soFar.slice( matched.length );
2045 // Return the length of the invalid excess
2046 // if we're just parsing
2047 // Otherwise, throw an error or return tokens
2051 Sizzle.error( selector ) :
2053 tokenCache( selector, groups ).slice( 0 );
2056 function toSelector( tokens ) {
2058 len = tokens.length,
2060 for ( ; i < len; i++ ) {
2061 selector += tokens[i].value;
2066 function addCombinator( matcher, combinator, base ) {
2067 var dir = combinator.dir,
2068 checkNonElements = base && dir === "parentNode",
2071 return combinator.first ?
2072 // Check against closest ancestor/preceding element
2073 function( elem, context, xml ) {
2074 while ( (elem = elem[ dir ]) ) {
2075 if ( elem.nodeType === 1 || checkNonElements ) {
2076 return matcher( elem, context, xml );
2081 // Check against all ancestor/preceding elements
2082 function( elem, context, xml ) {
2083 var oldCache, outerCache,
2084 newCache = [ dirruns, doneName ];
2086 // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
2088 while ( (elem = elem[ dir ]) ) {
2089 if ( elem.nodeType === 1 || checkNonElements ) {
2090 if ( matcher( elem, context, xml ) ) {
2096 while ( (elem = elem[ dir ]) ) {
2097 if ( elem.nodeType === 1 || checkNonElements ) {
2098 outerCache = elem[ expando ] || (elem[ expando ] = {});
2099 if ( (oldCache = outerCache[ dir ]) &&
2100 oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
2102 // Assign to newCache so results back-propagate to previous elements
2103 return (newCache[ 2 ] = oldCache[ 2 ]);
2105 // Reuse newcache so results back-propagate to previous elements
2106 outerCache[ dir ] = newCache;
2108 // A match means we're done; a fail means we have to keep checking
2109 if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
2119 function elementMatcher( matchers ) {
2120 return matchers.length > 1 ?
2121 function( elem, context, xml ) {
2122 var i = matchers.length;
2124 if ( !matchers[i]( elem, context, xml ) ) {
2133 function multipleContexts( selector, contexts, results ) {
2135 len = contexts.length;
2136 for ( ; i < len; i++ ) {
2137 Sizzle( selector, contexts[i], results );
2142 function condense( unmatched, map, filter, context, xml ) {
2146 len = unmatched.length,
2147 mapped = map != null;
2149 for ( ; i < len; i++ ) {
2150 if ( (elem = unmatched[i]) ) {
2151 if ( !filter || filter( elem, context, xml ) ) {
2152 newUnmatched.push( elem );
2160 return newUnmatched;
2163 function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
2164 if ( postFilter && !postFilter[ expando ] ) {
2165 postFilter = setMatcher( postFilter );
2167 if ( postFinder && !postFinder[ expando ] ) {
2168 postFinder = setMatcher( postFinder, postSelector );
2170 return markFunction(function( seed, results, context, xml ) {
2174 preexisting = results.length,
2176 // Get initial elements from seed or context
2177 elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
2179 // Prefilter to get matcher input, preserving a map for seed-results synchronization
2180 matcherIn = preFilter && ( seed || !selector ) ?
2181 condense( elems, preMap, preFilter, context, xml ) :
2184 matcherOut = matcher ?
2185 // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
2186 postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
2188 // ...intermediate processing is necessary
2191 // ...otherwise use results directly
2195 // Find primary matches
2197 matcher( matcherIn, matcherOut, context, xml );
2202 temp = condense( matcherOut, postMap );
2203 postFilter( temp, [], context, xml );
2205 // Un-match failing elements by moving them back to matcherIn
2208 if ( (elem = temp[i]) ) {
2209 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
2215 if ( postFinder || preFilter ) {
2217 // Get the final matcherOut by condensing this intermediate into postFinder contexts
2219 i = matcherOut.length;
2221 if ( (elem = matcherOut[i]) ) {
2222 // Restore matcherIn since elem is not yet a final match
2223 temp.push( (matcherIn[i] = elem) );
2226 postFinder( null, (matcherOut = []), temp, xml );
2229 // Move matched elements from seed to results to keep them synchronized
2230 i = matcherOut.length;
2232 if ( (elem = matcherOut[i]) &&
2233 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
2235 seed[temp] = !(results[temp] = elem);
2240 // Add elements to results, through postFinder if defined
2242 matcherOut = condense(
2243 matcherOut === results ?
2244 matcherOut.splice( preexisting, matcherOut.length ) :
2248 postFinder( null, results, matcherOut, xml );
2250 push.apply( results, matcherOut );
2256 function matcherFromTokens( tokens ) {
2257 var checkContext, matcher, j,
2258 len = tokens.length,
2259 leadingRelative = Expr.relative[ tokens[0].type ],
2260 implicitRelative = leadingRelative || Expr.relative[" "],
2261 i = leadingRelative ? 1 : 0,
2263 // The foundational matcher ensures that elements are reachable from top-level context(s)
2264 matchContext = addCombinator( function( elem ) {
2265 return elem === checkContext;
2266 }, implicitRelative, true ),
2267 matchAnyContext = addCombinator( function( elem ) {
2268 return indexOf.call( checkContext, elem ) > -1;
2269 }, implicitRelative, true ),
2270 matchers = [ function( elem, context, xml ) {
2271 return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
2272 (checkContext = context).nodeType ?
2273 matchContext( elem, context, xml ) :
2274 matchAnyContext( elem, context, xml ) );
2277 for ( ; i < len; i++ ) {
2278 if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
2279 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
2281 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
2283 // Return special upon seeing a positional matcher
2284 if ( matcher[ expando ] ) {
2285 // Find the next relative operator (if any) for proper handling
2287 for ( ; j < len; j++ ) {
2288 if ( Expr.relative[ tokens[j].type ] ) {
2293 i > 1 && elementMatcher( matchers ),
2294 i > 1 && toSelector(
2295 // If the preceding token was a descendant combinator, insert an implicit any-element `*`
2296 tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
2297 ).replace( rtrim, "$1" ),
2299 i < j && matcherFromTokens( tokens.slice( i, j ) ),
2300 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
2301 j < len && toSelector( tokens )
2304 matchers.push( matcher );
2308 return elementMatcher( matchers );
2311 function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
2312 var bySet = setMatchers.length > 0,
2313 byElement = elementMatchers.length > 0,
2314 superMatcher = function( seed, context, xml, results, outermost ) {
2315 var elem, j, matcher,
2318 unmatched = seed && [],
2320 contextBackup = outermostContext,
2321 // We must always have either seed elements or outermost context
2322 elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
2323 // Use integer dirruns iff this is the outermost matcher
2324 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
2328 outermostContext = context !== document && context;
2331 // Add elements passing elementMatchers directly to results
2332 // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
2333 // Support: IE<9, Safari
2334 // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
2335 for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
2336 if ( byElement && elem ) {
2338 while ( (matcher = elementMatchers[j++]) ) {
2339 if ( matcher( elem, context, xml ) ) {
2340 results.push( elem );
2345 dirruns = dirrunsUnique;
2349 // Track unmatched elements for set filters
2351 // They will have gone through all possible matchers
2352 if ( (elem = !matcher && elem) ) {
2356 // Lengthen the array for every element, matched or not
2358 unmatched.push( elem );
2363 // Apply set filters to unmatched elements
2365 if ( bySet && i !== matchedCount ) {
2367 while ( (matcher = setMatchers[j++]) ) {
2368 matcher( unmatched, setMatched, context, xml );
2372 // Reintegrate element matches to eliminate the need for sorting
2373 if ( matchedCount > 0 ) {
2375 if ( !(unmatched[i] || setMatched[i]) ) {
2376 setMatched[i] = pop.call( results );
2381 // Discard index placeholder values to get only actual matches
2382 setMatched = condense( setMatched );
2385 // Add matches to results
2386 push.apply( results, setMatched );
2388 // Seedless set matches succeeding multiple successful matchers stipulate sorting
2389 if ( outermost && !seed && setMatched.length > 0 &&
2390 ( matchedCount + setMatchers.length ) > 1 ) {
2392 Sizzle.uniqueSort( results );
2396 // Override manipulation of globals by nested matchers
2398 dirruns = dirrunsUnique;
2399 outermostContext = contextBackup;
2406 markFunction( superMatcher ) :
2410 compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
2413 elementMatchers = [],
2414 cached = compilerCache[ selector + " " ];
2417 // Generate a function of recursive functions that can be used to check each element
2419 match = tokenize( selector );
2423 cached = matcherFromTokens( match[i] );
2424 if ( cached[ expando ] ) {
2425 setMatchers.push( cached );
2427 elementMatchers.push( cached );
2431 // Cache the compiled function
2432 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
2434 // Save selector and tokenization
2435 cached.selector = selector;
2441 * A low-level selection function that works with Sizzle's compiled
2442 * selector functions
2443 * @param {String|Function} selector A selector or a pre-compiled
2444 * selector function built with Sizzle.compile
2445 * @param {Element} context
2446 * @param {Array} [results]
2447 * @param {Array} [seed] A set of elements to match against
2449 select = Sizzle.select = function( selector, context, results, seed ) {
2450 var i, tokens, token, type, find,
2451 compiled = typeof selector === "function" && selector,
2452 match = !seed && tokenize( (selector = compiled.selector || selector) );
2454 results = results || [];
2456 // Try to minimize operations if there is no seed and only one group
2457 if ( match.length === 1 ) {
2459 // Take a shortcut and set the context if the root selector is an ID
2460 tokens = match[0] = match[0].slice( 0 );
2461 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
2462 support.getById && context.nodeType === 9 && documentIsHTML &&
2463 Expr.relative[ tokens[1].type ] ) {
2465 context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
2469 // Precompiled matchers will still verify ancestry, so step up a level
2470 } else if ( compiled ) {
2471 context = context.parentNode;
2474 selector = selector.slice( tokens.shift().value.length );
2477 // Fetch a seed set for right-to-left matching
2478 i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
2482 // Abort if we hit a combinator
2483 if ( Expr.relative[ (type = token.type) ] ) {
2486 if ( (find = Expr.find[ type ]) ) {
2487 // Search, expanding context for leading sibling combinators
2489 token.matches[0].replace( runescape, funescape ),
2490 rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
2493 // If seed is empty or no tokens remain, we can return early
2494 tokens.splice( i, 1 );
2495 selector = seed.length && toSelector( tokens );
2497 push.apply( results, seed );
2507 // Compile and execute a filtering function if one is not provided
2508 // Provide `match` to avoid retokenization if we modified the selector above
2509 ( compiled || compile( selector, match ) )(
2514 rsibling.test( selector ) && testContext( context.parentNode ) || context
2519 // One-time assignments
2522 support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
2524 // Support: Chrome<14
2525 // Always assume duplicates if they aren't passed to the comparison function
2526 support.detectDuplicates = !!hasDuplicate;
2528 // Initialize against the default document
2531 // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
2532 // Detached nodes confoundingly follow *each other*
2533 support.sortDetached = assert(function( div1 ) {
2534 // Should return 1, but returns 4 (following)
2535 return div1.compareDocumentPosition( document.createElement("div") ) & 1;
2539 // Prevent attribute/property "interpolation"
2540 // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
2541 if ( !assert(function( div ) {
2542 div.innerHTML = "<a href='#'></a>";
2543 return div.firstChild.getAttribute("href") === "#" ;
2545 addHandle( "type|href|height|width", function( elem, name, isXML ) {
2547 return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
2553 // Use defaultValue in place of getAttribute("value")
2554 if ( !support.attributes || !assert(function( div ) {
2555 div.innerHTML = "<input/>";
2556 div.firstChild.setAttribute( "value", "" );
2557 return div.firstChild.getAttribute( "value" ) === "";
2559 addHandle( "value", function( elem, name, isXML ) {
2560 if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
2561 return elem.defaultValue;
2567 // Use getAttributeNode to fetch booleans when getAttribute lies
2568 if ( !assert(function( div ) {
2569 return div.getAttribute("disabled") == null;
2571 addHandle( booleans, function( elem, name, isXML ) {
2574 return elem[ name ] === true ? name.toLowerCase() :
2575 (val = elem.getAttributeNode( name )) && val.specified ?
2588 jQuery.find = Sizzle;
2589 jQuery.expr = Sizzle.selectors;
2590 jQuery.expr[":"] = jQuery.expr.pseudos;
2591 jQuery.unique = Sizzle.uniqueSort;
2592 jQuery.text = Sizzle.getText;
2593 jQuery.isXMLDoc = Sizzle.isXML;
2594 jQuery.contains = Sizzle.contains;
2598 var rneedsContext = jQuery.expr.match.needsContext;
2600 var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
2604 var risSimple = /^.[^:#\[\.,]*$/;
2606 // Implement the identical functionality for filter and not
2607 function winnow( elements, qualifier, not ) {
2608 if ( jQuery.isFunction( qualifier ) ) {
2609 return jQuery.grep( elements, function( elem, i ) {
2611 return !!qualifier.call( elem, i, elem ) !== not;
2616 if ( qualifier.nodeType ) {
2617 return jQuery.grep( elements, function( elem ) {
2618 return ( elem === qualifier ) !== not;
2623 if ( typeof qualifier === "string" ) {
2624 if ( risSimple.test( qualifier ) ) {
2625 return jQuery.filter( qualifier, elements, not );
2628 qualifier = jQuery.filter( qualifier, elements );
2631 return jQuery.grep( elements, function( elem ) {
2632 return ( indexOf.call( qualifier, elem ) >= 0 ) !== not;
2636 jQuery.filter = function( expr, elems, not ) {
2637 var elem = elems[ 0 ];
2640 expr = ":not(" + expr + ")";
2643 return elems.length === 1 && elem.nodeType === 1 ?
2644 jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
2645 jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
2646 return elem.nodeType === 1;
2651 find: function( selector ) {
2657 if ( typeof selector !== "string" ) {
2658 return this.pushStack( jQuery( selector ).filter(function() {
2659 for ( i = 0; i < len; i++ ) {
2660 if ( jQuery.contains( self[ i ], this ) ) {
2667 for ( i = 0; i < len; i++ ) {
2668 jQuery.find( selector, self[ i ], ret );
2671 // Needed because $( selector, context ) becomes $( context ).find( selector )
2672 ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
2673 ret.selector = this.selector ? this.selector + " " + selector : selector;
2676 filter: function( selector ) {
2677 return this.pushStack( winnow(this, selector || [], false) );
2679 not: function( selector ) {
2680 return this.pushStack( winnow(this, selector || [], true) );
2682 is: function( selector ) {
2686 // If this is a positional/relative selector, check membership in the returned set
2687 // so $("p:first").is("p:last") won't return true for a doc with two "p".
2688 typeof selector === "string" && rneedsContext.test( selector ) ?
2689 jQuery( selector ) :
2697 // Initialize a jQuery object
2700 // A central reference to the root jQuery(document)
2703 // A simple way to check for HTML strings
2704 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
2705 // Strict HTML recognition (#11290: must start with <)
2706 rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
2708 init = jQuery.fn.init = function( selector, context ) {
2711 // HANDLE: $(""), $(null), $(undefined), $(false)
2716 // Handle HTML strings
2717 if ( typeof selector === "string" ) {
2718 if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
2719 // Assume that strings that start and end with <> are HTML and skip the regex check
2720 match = [ null, selector, null ];
2723 match = rquickExpr.exec( selector );
2726 // Match html or make sure no context is specified for #id
2727 if ( match && (match[1] || !context) ) {
2729 // HANDLE: $(html) -> $(array)
2731 context = context instanceof jQuery ? context[0] : context;
2733 // scripts is true for back-compat
2734 // Intentionally let the error be thrown if parseHTML is not present
2735 jQuery.merge( this, jQuery.parseHTML(
2737 context && context.nodeType ? context.ownerDocument || context : document,
2741 // HANDLE: $(html, props)
2742 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
2743 for ( match in context ) {
2744 // Properties of context are called as methods if possible
2745 if ( jQuery.isFunction( this[ match ] ) ) {
2746 this[ match ]( context[ match ] );
2748 // ...and otherwise set as attributes
2750 this.attr( match, context[ match ] );
2759 elem = document.getElementById( match[2] );
2761 // Check parentNode to catch when Blackberry 4.6 returns
2762 // nodes that are no longer in the document #6963
2763 if ( elem && elem.parentNode ) {
2764 // Inject the element directly into the jQuery object
2769 this.context = document;
2770 this.selector = selector;
2774 // HANDLE: $(expr, $(...))
2775 } else if ( !context || context.jquery ) {
2776 return ( context || rootjQuery ).find( selector );
2778 // HANDLE: $(expr, context)
2779 // (which is just equivalent to: $(context).find(expr)
2781 return this.constructor( context ).find( selector );
2784 // HANDLE: $(DOMElement)
2785 } else if ( selector.nodeType ) {
2786 this.context = this[0] = selector;
2790 // HANDLE: $(function)
2791 // Shortcut for document ready
2792 } else if ( jQuery.isFunction( selector ) ) {
2793 return typeof rootjQuery.ready !== "undefined" ?
2794 rootjQuery.ready( selector ) :
2795 // Execute immediately if ready is not present
2799 if ( selector.selector !== undefined ) {
2800 this.selector = selector.selector;
2801 this.context = selector.context;
2804 return jQuery.makeArray( selector, this );
2807 // Give the init function the jQuery prototype for later instantiation
2808 init.prototype = jQuery.fn;
2810 // Initialize central reference
2811 rootjQuery = jQuery( document );
2814 var rparentsprev = /^(?:parents|prev(?:Until|All))/,
2815 // methods guaranteed to produce a unique set when starting from a unique set
2816 guaranteedUnique = {
2824 dir: function( elem, dir, until ) {
2826 truncate = until !== undefined;
2828 while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
2829 if ( elem.nodeType === 1 ) {
2830 if ( truncate && jQuery( elem ).is( until ) ) {
2833 matched.push( elem );
2839 sibling: function( n, elem ) {
2842 for ( ; n; n = n.nextSibling ) {
2843 if ( n.nodeType === 1 && n !== elem ) {
2853 has: function( target ) {
2854 var targets = jQuery( target, this ),
2857 return this.filter(function() {
2859 for ( ; i < l; i++ ) {
2860 if ( jQuery.contains( this, targets[i] ) ) {
2867 closest: function( selectors, context ) {
2872 pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
2873 jQuery( selectors, context || this.context ) :
2876 for ( ; i < l; i++ ) {
2877 for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
2878 // Always skip document fragments
2879 if ( cur.nodeType < 11 && (pos ?
2880 pos.index(cur) > -1 :
2882 // Don't pass non-elements to Sizzle
2883 cur.nodeType === 1 &&
2884 jQuery.find.matchesSelector(cur, selectors)) ) {
2886 matched.push( cur );
2892 return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
2895 // Determine the position of an element within
2896 // the matched set of elements
2897 index: function( elem ) {
2899 // No argument, return index in parent
2901 return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
2904 // index in selector
2905 if ( typeof elem === "string" ) {
2906 return indexOf.call( jQuery( elem ), this[ 0 ] );
2909 // Locate the position of the desired element
2910 return indexOf.call( this,
2912 // If it receives a jQuery object, the first element is used
2913 elem.jquery ? elem[ 0 ] : elem
2917 add: function( selector, context ) {
2918 return this.pushStack(
2920 jQuery.merge( this.get(), jQuery( selector, context ) )
2925 addBack: function( selector ) {
2926 return this.add( selector == null ?
2927 this.prevObject : this.prevObject.filter(selector)
2932 function sibling( cur, dir ) {
2933 while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
2938 parent: function( elem ) {
2939 var parent = elem.parentNode;
2940 return parent && parent.nodeType !== 11 ? parent : null;
2942 parents: function( elem ) {
2943 return jQuery.dir( elem, "parentNode" );
2945 parentsUntil: function( elem, i, until ) {
2946 return jQuery.dir( elem, "parentNode", until );
2948 next: function( elem ) {
2949 return sibling( elem, "nextSibling" );
2951 prev: function( elem ) {
2952 return sibling( elem, "previousSibling" );
2954 nextAll: function( elem ) {
2955 return jQuery.dir( elem, "nextSibling" );
2957 prevAll: function( elem ) {
2958 return jQuery.dir( elem, "previousSibling" );
2960 nextUntil: function( elem, i, until ) {
2961 return jQuery.dir( elem, "nextSibling", until );
2963 prevUntil: function( elem, i, until ) {
2964 return jQuery.dir( elem, "previousSibling", until );
2966 siblings: function( elem ) {
2967 return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
2969 children: function( elem ) {
2970 return jQuery.sibling( elem.firstChild );
2972 contents: function( elem ) {
2973 return elem.contentDocument || jQuery.merge( [], elem.childNodes );
2975 }, function( name, fn ) {
2976 jQuery.fn[ name ] = function( until, selector ) {
2977 var matched = jQuery.map( this, fn, until );
2979 if ( name.slice( -5 ) !== "Until" ) {
2983 if ( selector && typeof selector === "string" ) {
2984 matched = jQuery.filter( selector, matched );
2987 if ( this.length > 1 ) {
2988 // Remove duplicates
2989 if ( !guaranteedUnique[ name ] ) {
2990 jQuery.unique( matched );
2993 // Reverse order for parents* and prev-derivatives
2994 if ( rparentsprev.test( name ) ) {
2999 return this.pushStack( matched );
3002 var rnotwhite = (/\S+/g);
3006 // String to Object options format cache
3007 var optionsCache = {};
3009 // Convert String-formatted options into Object-formatted ones and store in cache
3010 function createOptions( options ) {
3011 var object = optionsCache[ options ] = {};
3012 jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
3013 object[ flag ] = true;
3019 * Create a callback list using the following parameters:
3021 * options: an optional list of space-separated options that will change how
3022 * the callback list behaves or a more traditional option object
3024 * By default a callback list will act like an event callback list and can be
3025 * "fired" multiple times.
3029 * once: will ensure the callback list can only be fired once (like a Deferred)
3031 * memory: will keep track of previous values and will call any callback added
3032 * after the list has been fired right away with the latest "memorized"
3033 * values (like a Deferred)
3035 * unique: will ensure a callback can only be added once (no duplicate in the list)
3037 * stopOnFalse: interrupt callings when a callback returns false
3040 jQuery.Callbacks = function( options ) {
3042 // Convert options from String-formatted to Object-formatted if needed
3043 // (we check in cache first)
3044 options = typeof options === "string" ?
3045 ( optionsCache[ options ] || createOptions( options ) ) :
3046 jQuery.extend( {}, options );
3048 var // Last fire value (for non-forgettable lists)
3050 // Flag to know if list was already fired
3052 // Flag to know if list is currently firing
3054 // First callback to fire (used internally by add and fireWith)
3056 // End of the loop when firing
3058 // Index of currently firing callback (modified by remove if needed)
3060 // Actual callback list
3062 // Stack of fire calls for repeatable lists
3063 stack = !options.once && [],
3065 fire = function( data ) {
3066 memory = options.memory && data;
3068 firingIndex = firingStart || 0;
3070 firingLength = list.length;
3072 for ( ; list && firingIndex < firingLength; firingIndex++ ) {
3073 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
3074 memory = false; // To prevent further calls using add
3081 if ( stack.length ) {
3082 fire( stack.shift() );
3084 } else if ( memory ) {
3091 // Actual Callbacks object
3093 // Add a callback or a collection of callbacks to the list
3096 // First, we save the current length
3097 var start = list.length;
3098 (function add( args ) {
3099 jQuery.each( args, function( _, arg ) {
3100 var type = jQuery.type( arg );
3101 if ( type === "function" ) {
3102 if ( !options.unique || !self.has( arg ) ) {
3105 } else if ( arg && arg.length && type !== "string" ) {
3106 // Inspect recursively
3111 // Do we need to add the callbacks to the
3112 // current firing batch?
3114 firingLength = list.length;
3115 // With memory, if we're not firing then
3116 // we should call right away
3117 } else if ( memory ) {
3118 firingStart = start;
3124 // Remove a callback from the list
3125 remove: function() {
3127 jQuery.each( arguments, function( _, arg ) {
3129 while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
3130 list.splice( index, 1 );
3131 // Handle firing indexes
3133 if ( index <= firingLength ) {
3136 if ( index <= firingIndex ) {
3145 // Check if a given callback is in the list.
3146 // If no argument is given, return whether or not list has callbacks attached.
3147 has: function( fn ) {
3148 return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
3150 // Remove all callbacks from the list
3156 // Have the list do nothing anymore
3157 disable: function() {
3158 list = stack = memory = undefined;
3162 disabled: function() {
3165 // Lock the list in its current state
3174 locked: function() {
3177 // Call all callbacks with the given context and arguments
3178 fireWith: function( context, args ) {
3179 if ( list && ( !fired || stack ) ) {
3181 args = [ context, args.slice ? args.slice() : args ];
3190 // Call all the callbacks with the given arguments
3192 self.fireWith( this, arguments );
3195 // To know if the callbacks have already been called at least once
3207 Deferred: function( func ) {
3209 // action, add listener, listener list, final state
3210 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
3211 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
3212 [ "notify", "progress", jQuery.Callbacks("memory") ]
3219 always: function() {
3220 deferred.done( arguments ).fail( arguments );
3223 then: function( /* fnDone, fnFail, fnProgress */ ) {
3224 var fns = arguments;
3225 return jQuery.Deferred(function( newDefer ) {
3226 jQuery.each( tuples, function( i, tuple ) {
3227 var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
3228 // deferred[ done | fail | progress ] for forwarding actions to newDefer
3229 deferred[ tuple[1] ](function() {
3230 var returned = fn && fn.apply( this, arguments );
3231 if ( returned && jQuery.isFunction( returned.promise ) ) {
3233 .done( newDefer.resolve )
3234 .fail( newDefer.reject )
3235 .progress( newDefer.notify );
3237 newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
3244 // Get a promise for this deferred
3245 // If obj is provided, the promise aspect is added to the object
3246 promise: function( obj ) {
3247 return obj != null ? jQuery.extend( obj, promise ) : promise;
3252 // Keep pipe for back-compat
3253 promise.pipe = promise.then;
3255 // Add list-specific methods
3256 jQuery.each( tuples, function( i, tuple ) {
3257 var list = tuple[ 2 ],
3258 stateString = tuple[ 3 ];
3260 // promise[ done | fail | progress ] = list.add
3261 promise[ tuple[1] ] = list.add;
3264 if ( stateString ) {
3265 list.add(function() {
3266 // state = [ resolved | rejected ]
3267 state = stateString;
3269 // [ reject_list | resolve_list ].disable; progress_list.lock
3270 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
3273 // deferred[ resolve | reject | notify ]
3274 deferred[ tuple[0] ] = function() {
3275 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
3278 deferred[ tuple[0] + "With" ] = list.fireWith;
3281 // Make the deferred a promise
3282 promise.promise( deferred );
3284 // Call given func if any
3286 func.call( deferred, deferred );
3294 when: function( subordinate /* , ..., subordinateN */ ) {
3296 resolveValues = slice.call( arguments ),
3297 length = resolveValues.length,
3299 // the count of uncompleted subordinates
3300 remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
3302 // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
3303 deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
3305 // Update function for both resolve and progress values
3306 updateFunc = function( i, contexts, values ) {
3307 return function( value ) {
3308 contexts[ i ] = this;
3309 values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
3310 if ( values === progressValues ) {
3311 deferred.notifyWith( contexts, values );
3312 } else if ( !( --remaining ) ) {
3313 deferred.resolveWith( contexts, values );
3318 progressValues, progressContexts, resolveContexts;
3320 // add listeners to Deferred subordinates; treat others as resolved
3322 progressValues = new Array( length );
3323 progressContexts = new Array( length );
3324 resolveContexts = new Array( length );
3325 for ( ; i < length; i++ ) {
3326 if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
3327 resolveValues[ i ].promise()
3328 .done( updateFunc( i, resolveContexts, resolveValues ) )
3329 .fail( deferred.reject )
3330 .progress( updateFunc( i, progressContexts, progressValues ) );
3337 // if we're not waiting on anything, resolve the master
3339 deferred.resolveWith( resolveContexts, resolveValues );
3342 return deferred.promise();
3347 // The deferred used on DOM ready
3350 jQuery.fn.ready = function( fn ) {
3352 jQuery.ready.promise().done( fn );
3358 // Is the DOM ready to be used? Set to true once it occurs.
3361 // A counter to track how many items to wait for before
3362 // the ready event fires. See #6781
3365 // Hold (or release) the ready event
3366 holdReady: function( hold ) {
3370 jQuery.ready( true );
3374 // Handle when the DOM is ready
3375 ready: function( wait ) {
3377 // Abort if there are pending holds or we're already ready
3378 if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
3382 // Remember that the DOM is ready
3383 jQuery.isReady = true;
3385 // If a normal DOM Ready event fired, decrement, and wait if need be
3386 if ( wait !== true && --jQuery.readyWait > 0 ) {
3390 // If there are functions bound, to execute
3391 readyList.resolveWith( document, [ jQuery ] );
3393 // Trigger any bound ready events
3394 if ( jQuery.fn.triggerHandler ) {
3395 jQuery( document ).triggerHandler( "ready" );
3396 jQuery( document ).off( "ready" );
3402 * The ready event handler and self cleanup method
3404 function completed() {
3405 document.removeEventListener( "DOMContentLoaded", completed, false );
3406 window.removeEventListener( "load", completed, false );
3410 jQuery.ready.promise = function( obj ) {
3413 readyList = jQuery.Deferred();
3415 // Catch cases where $(document).ready() is called after the browser event has already occurred.
3416 // we once tried to use readyState "interactive" here, but it caused issues like the one
3417 // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
3418 if ( document.readyState === "complete" ) {
3419 // Handle it asynchronously to allow scripts the opportunity to delay ready
3420 setTimeout( jQuery.ready );
3424 // Use the handy event callback
3425 document.addEventListener( "DOMContentLoaded", completed, false );
3427 // A fallback to window.onload, that will always work
3428 window.addEventListener( "load", completed, false );
3431 return readyList.promise( obj );
3434 // Kick off the DOM ready check even if the user does not
3435 jQuery.ready.promise();
3440 // Multifunctional method to get and set values of a collection
3441 // The value/s can optionally be executed if it's a function
3442 var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
3448 if ( jQuery.type( key ) === "object" ) {
3451 jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
3455 } else if ( value !== undefined ) {
3458 if ( !jQuery.isFunction( value ) ) {
3463 // Bulk operations run against the entire set
3465 fn.call( elems, value );
3468 // ...except when executing function values
3471 fn = function( elem, key, value ) {
3472 return bulk.call( jQuery( elem ), value );
3478 for ( ; i < len; i++ ) {
3479 fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
3490 len ? fn( elems[0], key ) : emptyGet;
3495 * Determines whether an object can have data
3497 jQuery.acceptData = function( owner ) {
3500 // - Node.ELEMENT_NODE
3501 // - Node.DOCUMENT_NODE
3505 return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
3510 // Support: Android < 4,
3511 // Old WebKit does not have Object.preventExtensions/freeze method,
3512 // return new empty object instead with no [[set]] accessor
3513 Object.defineProperty( this.cache = {}, 0, {
3519 this.expando = jQuery.expando + Math.random();
3523 Data.accepts = jQuery.acceptData;
3526 key: function( owner ) {
3527 // We can accept data for non-element nodes in modern browsers,
3528 // but we should not, see #8335.
3529 // Always return the key for a frozen object.
3530 if ( !Data.accepts( owner ) ) {
3534 var descriptor = {},
3535 // Check if the owner object already has a cache key
3536 unlock = owner[ this.expando ];
3538 // If not, create one
3540 unlock = Data.uid++;
3542 // Secure it in a non-enumerable, non-writable property
3544 descriptor[ this.expando ] = { value: unlock };
3545 Object.defineProperties( owner, descriptor );
3547 // Support: Android < 4
3548 // Fallback to a less secure definition
3550 descriptor[ this.expando ] = unlock;
3551 jQuery.extend( owner, descriptor );
3555 // Ensure the cache object
3556 if ( !this.cache[ unlock ] ) {
3557 this.cache[ unlock ] = {};
3562 set: function( owner, data, value ) {
3564 // There may be an unlock assigned to this node,
3565 // if there is no entry for this "owner", create one inline
3566 // and set the unlock as though an owner entry had always existed
3567 unlock = this.key( owner ),
3568 cache = this.cache[ unlock ];
3570 // Handle: [ owner, key, value ] args
3571 if ( typeof data === "string" ) {
3572 cache[ data ] = value;
3574 // Handle: [ owner, { properties } ] args
3576 // Fresh assignments by object are shallow copied
3577 if ( jQuery.isEmptyObject( cache ) ) {
3578 jQuery.extend( this.cache[ unlock ], data );
3579 // Otherwise, copy the properties one-by-one to the cache object
3581 for ( prop in data ) {
3582 cache[ prop ] = data[ prop ];
3588 get: function( owner, key ) {
3589 // Either a valid cache is found, or will be created.
3590 // New caches will be created and the unlock returned,
3591 // allowing direct access to the newly created
3592 // empty data object. A valid owner object must be provided.
3593 var cache = this.cache[ this.key( owner ) ];
3595 return key === undefined ?
3596 cache : cache[ key ];
3598 access: function( owner, key, value ) {
3600 // In cases where either:
3602 // 1. No key was specified
3603 // 2. A string key was specified, but no value provided
3605 // Take the "read" path and allow the get method to determine
3606 // which value to return, respectively either:
3608 // 1. The entire cache object
3609 // 2. The data stored at the key
3611 if ( key === undefined ||
3612 ((key && typeof key === "string") && value === undefined) ) {
3614 stored = this.get( owner, key );
3616 return stored !== undefined ?
3617 stored : this.get( owner, jQuery.camelCase(key) );
3620 // [*]When the key is not a string, or both a key and value
3621 // are specified, set or extend (existing objects) with either:
3623 // 1. An object of properties
3624 // 2. A key and value
3626 this.set( owner, key, value );
3628 // Since the "set" path can have two possible entry points
3629 // return the expected data based on which path was taken[*]
3630 return value !== undefined ? value : key;
3632 remove: function( owner, key ) {
3634 unlock = this.key( owner ),
3635 cache = this.cache[ unlock ];
3637 if ( key === undefined ) {
3638 this.cache[ unlock ] = {};
3641 // Support array or space separated string of keys
3642 if ( jQuery.isArray( key ) ) {
3643 // If "name" is an array of keys...
3644 // When data is initially created, via ("key", "val") signature,
3645 // keys will be converted to camelCase.
3646 // Since there is no way to tell _how_ a key was added, remove
3647 // both plain key and camelCase key. #12786
3648 // This will only penalize the array argument path.
3649 name = key.concat( key.map( jQuery.camelCase ) );
3651 camel = jQuery.camelCase( key );
3652 // Try the string as a key before any manipulation
3653 if ( key in cache ) {
3654 name = [ key, camel ];
3656 // If a key with the spaces exists, use it.
3657 // Otherwise, create an array by matching non-whitespace
3659 name = name in cache ?
3660 [ name ] : ( name.match( rnotwhite ) || [] );
3666 delete cache[ name[ i ] ];
3670 hasData: function( owner ) {
3671 return !jQuery.isEmptyObject(
3672 this.cache[ owner[ this.expando ] ] || {}
3675 discard: function( owner ) {
3676 if ( owner[ this.expando ] ) {
3677 delete this.cache[ owner[ this.expando ] ];
3681 var data_priv = new Data();
3683 var data_user = new Data();
3688 Implementation Summary
3690 1. Enforce API surface and semantic compatibility with 1.9.x branch
3691 2. Improve the module's maintainability by reducing the storage
3692 paths to a single mechanism.
3693 3. Use the same single mechanism to support "private" and "user" data.
3694 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
3695 5. Avoid exposing implementation details on user objects (eg. expando properties)
3696 6. Provide a clear path for implementation upgrade to WeakMap in 2014
3698 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
3699 rmultiDash = /([A-Z])/g;
3701 function dataAttr( elem, key, data ) {
3704 // If nothing was found internally, try to fetch any
3705 // data from the HTML5 data-* attribute
3706 if ( data === undefined && elem.nodeType === 1 ) {
3707 name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
3708 data = elem.getAttribute( name );
3710 if ( typeof data === "string" ) {
3712 data = data === "true" ? true :
3713 data === "false" ? false :
3714 data === "null" ? null :
3715 // Only convert to a number if it doesn't change the string
3716 +data + "" === data ? +data :
3717 rbrace.test( data ) ? jQuery.parseJSON( data ) :
3721 // Make sure we set the data so it isn't changed later
3722 data_user.set( elem, key, data );
3731 hasData: function( elem ) {
3732 return data_user.hasData( elem ) || data_priv.hasData( elem );
3735 data: function( elem, name, data ) {
3736 return data_user.access( elem, name, data );
3739 removeData: function( elem, name ) {
3740 data_user.remove( elem, name );
3743 // TODO: Now that all calls to _data and _removeData have been replaced
3744 // with direct calls to data_priv methods, these can be deprecated.
3745 _data: function( elem, name, data ) {
3746 return data_priv.access( elem, name, data );
3749 _removeData: function( elem, name ) {
3750 data_priv.remove( elem, name );
3755 data: function( key, value ) {
3758 attrs = elem && elem.attributes;
3761 if ( key === undefined ) {
3762 if ( this.length ) {
3763 data = data_user.get( elem );
3765 if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
3770 // The attrs elements can be null (#14894)
3772 name = attrs[ i ].name;
3773 if ( name.indexOf( "data-" ) === 0 ) {
3774 name = jQuery.camelCase( name.slice(5) );
3775 dataAttr( elem, name, data[ name ] );
3779 data_priv.set( elem, "hasDataAttrs", true );
3786 // Sets multiple values
3787 if ( typeof key === "object" ) {
3788 return this.each(function() {
3789 data_user.set( this, key );
3793 return access( this, function( value ) {
3795 camelKey = jQuery.camelCase( key );
3797 // The calling jQuery object (element matches) is not empty
3798 // (and therefore has an element appears at this[ 0 ]) and the
3799 // `value` parameter was not undefined. An empty jQuery object
3800 // will result in `undefined` for elem = this[ 0 ] which will
3801 // throw an exception if an attempt to read a data cache is made.
3802 if ( elem && value === undefined ) {
3803 // Attempt to get data from the cache
3804 // with the key as-is
3805 data = data_user.get( elem, key );
3806 if ( data !== undefined ) {
3810 // Attempt to get data from the cache
3811 // with the key camelized
3812 data = data_user.get( elem, camelKey );
3813 if ( data !== undefined ) {
3817 // Attempt to "discover" the data in
3818 // HTML5 custom data-* attrs
3819 data = dataAttr( elem, camelKey, undefined );
3820 if ( data !== undefined ) {
3824 // We tried really hard, but the data doesn't exist.
3829 this.each(function() {
3830 // First, attempt to store a copy or reference of any
3831 // data that might've been store with a camelCased key.
3832 var data = data_user.get( this, camelKey );
3834 // For HTML5 data-* attribute interop, we have to
3835 // store property names with dashes in a camelCase form.
3836 // This might not apply to all properties...*
3837 data_user.set( this, camelKey, value );
3839 // *... In the case of properties that might _actually_
3840 // have dashes, we need to also store a copy of that
3841 // unchanged property.
3842 if ( key.indexOf("-") !== -1 && data !== undefined ) {
3843 data_user.set( this, key, value );
3846 }, null, value, arguments.length > 1, null, true );
3849 removeData: function( key ) {
3850 return this.each(function() {
3851 data_user.remove( this, key );
3858 queue: function( elem, type, data ) {
3862 type = ( type || "fx" ) + "queue";
3863 queue = data_priv.get( elem, type );
3865 // Speed up dequeue by getting out quickly if this is just a lookup
3867 if ( !queue || jQuery.isArray( data ) ) {
3868 queue = data_priv.access( elem, type, jQuery.makeArray(data) );
3877 dequeue: function( elem, type ) {
3878 type = type || "fx";
3880 var queue = jQuery.queue( elem, type ),
3881 startLength = queue.length,
3883 hooks = jQuery._queueHooks( elem, type ),
3885 jQuery.dequeue( elem, type );
3888 // If the fx queue is dequeued, always remove the progress sentinel
3889 if ( fn === "inprogress" ) {
3896 // Add a progress sentinel to prevent the fx queue from being
3897 // automatically dequeued
3898 if ( type === "fx" ) {
3899 queue.unshift( "inprogress" );
3902 // clear up the last queue stop function
3904 fn.call( elem, next, hooks );
3907 if ( !startLength && hooks ) {
3912 // not intended for public consumption - generates a queueHooks object, or returns the current one
3913 _queueHooks: function( elem, type ) {
3914 var key = type + "queueHooks";
3915 return data_priv.get( elem, key ) || data_priv.access( elem, key, {
3916 empty: jQuery.Callbacks("once memory").add(function() {
3917 data_priv.remove( elem, [ type + "queue", key ] );
3924 queue: function( type, data ) {
3927 if ( typeof type !== "string" ) {
3933 if ( arguments.length < setter ) {
3934 return jQuery.queue( this[0], type );
3937 return data === undefined ?
3939 this.each(function() {
3940 var queue = jQuery.queue( this, type, data );
3942 // ensure a hooks for this queue
3943 jQuery._queueHooks( this, type );
3945 if ( type === "fx" && queue[0] !== "inprogress" ) {
3946 jQuery.dequeue( this, type );
3950 dequeue: function( type ) {
3951 return this.each(function() {
3952 jQuery.dequeue( this, type );
3955 clearQueue: function( type ) {
3956 return this.queue( type || "fx", [] );
3958 // Get a promise resolved when queues of a certain type
3959 // are emptied (fx is the type by default)
3960 promise: function( type, obj ) {
3963 defer = jQuery.Deferred(),
3966 resolve = function() {
3967 if ( !( --count ) ) {
3968 defer.resolveWith( elements, [ elements ] );
3972 if ( typeof type !== "string" ) {
3976 type = type || "fx";
3979 tmp = data_priv.get( elements[ i ], type + "queueHooks" );
3980 if ( tmp && tmp.empty ) {
3982 tmp.empty.add( resolve );
3986 return defer.promise( obj );
3989 var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
3991 var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
3993 var isHidden = function( elem, el ) {
3994 // isHidden might be called from jQuery#filter function;
3995 // in that case, element will be second argument
3997 return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
4000 var rcheckableType = (/^(?:checkbox|radio)$/i);
4005 var fragment = document.createDocumentFragment(),
4006 div = fragment.appendChild( document.createElement( "div" ) ),
4007 input = document.createElement( "input" );
4009 // #11217 - WebKit loses check when the name is after the checked attribute
4010 // Support: Windows Web Apps (WWA)
4011 // `name` and `type` need .setAttribute for WWA
4012 input.setAttribute( "type", "radio" );
4013 input.setAttribute( "checked", "checked" );
4014 input.setAttribute( "name", "t" );
4016 div.appendChild( input );
4018 // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
4019 // old WebKit doesn't clone checked state correctly in fragments
4020 support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
4022 // Make sure textarea (and checkbox) defaultValue is properly cloned
4023 // Support: IE9-IE11+
4024 div.innerHTML = "<textarea>x</textarea>";
4025 support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
4027 var strundefined = typeof undefined;
4031 support.focusinBubbles = "onfocusin" in window;
4036 rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
4037 rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
4038 rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
4040 function returnTrue() {
4044 function returnFalse() {
4048 function safeActiveElement() {
4050 return document.activeElement;
4055 * Helper functions for managing events -- not part of the public interface.
4056 * Props to Dean Edwards' addEvent library for many of the ideas.
4062 add: function( elem, types, handler, data, selector ) {
4064 var handleObjIn, eventHandle, tmp,
4065 events, t, handleObj,
4066 special, handlers, type, namespaces, origType,
4067 elemData = data_priv.get( elem );
4069 // Don't attach events to noData or text/comment nodes (but allow plain objects)
4074 // Caller can pass in an object of custom data in lieu of the handler
4075 if ( handler.handler ) {
4076 handleObjIn = handler;
4077 handler = handleObjIn.handler;
4078 selector = handleObjIn.selector;
4081 // Make sure that the handler has a unique ID, used to find/remove it later
4082 if ( !handler.guid ) {
4083 handler.guid = jQuery.guid++;
4086 // Init the element's event structure and main handler, if this is the first
4087 if ( !(events = elemData.events) ) {
4088 events = elemData.events = {};
4090 if ( !(eventHandle = elemData.handle) ) {
4091 eventHandle = elemData.handle = function( e ) {
4092 // Discard the second event of a jQuery.event.trigger() and
4093 // when an event is called after a page has unloaded
4094 return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
4095 jQuery.event.dispatch.apply( elem, arguments ) : undefined;
4099 // Handle multiple events separated by a space
4100 types = ( types || "" ).match( rnotwhite ) || [ "" ];
4103 tmp = rtypenamespace.exec( types[t] ) || [];
4104 type = origType = tmp[1];
4105 namespaces = ( tmp[2] || "" ).split( "." ).sort();
4107 // There *must* be a type, no attaching namespace-only handlers
4112 // If event changes its type, use the special event handlers for the changed type
4113 special = jQuery.event.special[ type ] || {};
4115 // If selector defined, determine special event api type, otherwise given type
4116 type = ( selector ? special.delegateType : special.bindType ) || type;
4118 // Update special based on newly reset type
4119 special = jQuery.event.special[ type ] || {};
4121 // handleObj is passed to all event handlers
4122 handleObj = jQuery.extend({
4129 needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
4130 namespace: namespaces.join(".")
4133 // Init the event handler queue if we're the first
4134 if ( !(handlers = events[ type ]) ) {
4135 handlers = events[ type ] = [];
4136 handlers.delegateCount = 0;
4138 // Only use addEventListener if the special events handler returns false
4139 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
4140 if ( elem.addEventListener ) {
4141 elem.addEventListener( type, eventHandle, false );
4146 if ( special.add ) {
4147 special.add.call( elem, handleObj );
4149 if ( !handleObj.handler.guid ) {
4150 handleObj.handler.guid = handler.guid;
4154 // Add to the element's handler list, delegates in front
4156 handlers.splice( handlers.delegateCount++, 0, handleObj );
4158 handlers.push( handleObj );
4161 // Keep track of which events have ever been used, for event optimization
4162 jQuery.event.global[ type ] = true;
4167 // Detach an event or set of events from an element
4168 remove: function( elem, types, handler, selector, mappedTypes ) {
4170 var j, origCount, tmp,
4171 events, t, handleObj,
4172 special, handlers, type, namespaces, origType,
4173 elemData = data_priv.hasData( elem ) && data_priv.get( elem );
4175 if ( !elemData || !(events = elemData.events) ) {
4179 // Once for each type.namespace in types; type may be omitted
4180 types = ( types || "" ).match( rnotwhite ) || [ "" ];
4183 tmp = rtypenamespace.exec( types[t] ) || [];
4184 type = origType = tmp[1];
4185 namespaces = ( tmp[2] || "" ).split( "." ).sort();
4187 // Unbind all events (on this namespace, if provided) for the element
4189 for ( type in events ) {
4190 jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
4195 special = jQuery.event.special[ type ] || {};
4196 type = ( selector ? special.delegateType : special.bindType ) || type;
4197 handlers = events[ type ] || [];
4198 tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
4200 // Remove matching events
4201 origCount = j = handlers.length;
4203 handleObj = handlers[ j ];
4205 if ( ( mappedTypes || origType === handleObj.origType ) &&
4206 ( !handler || handler.guid === handleObj.guid ) &&
4207 ( !tmp || tmp.test( handleObj.namespace ) ) &&
4208 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
4209 handlers.splice( j, 1 );
4211 if ( handleObj.selector ) {
4212 handlers.delegateCount--;
4214 if ( special.remove ) {
4215 special.remove.call( elem, handleObj );
4220 // Remove generic event handler if we removed something and no more handlers exist
4221 // (avoids potential for endless recursion during removal of special event handlers)
4222 if ( origCount && !handlers.length ) {
4223 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
4224 jQuery.removeEvent( elem, type, elemData.handle );
4227 delete events[ type ];
4231 // Remove the expando if it's no longer used
4232 if ( jQuery.isEmptyObject( events ) ) {
4233 delete elemData.handle;
4234 data_priv.remove( elem, "events" );
4238 trigger: function( event, data, elem, onlyHandlers ) {
4240 var i, cur, tmp, bubbleType, ontype, handle, special,
4241 eventPath = [ elem || document ],
4242 type = hasOwn.call( event, "type" ) ? event.type : event,
4243 namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
4245 cur = tmp = elem = elem || document;
4247 // Don't do events on text and comment nodes
4248 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
4252 // focus/blur morphs to focusin/out; ensure we're not firing them right now
4253 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
4257 if ( type.indexOf(".") >= 0 ) {
4258 // Namespaced trigger; create a regexp to match event type in handle()
4259 namespaces = type.split(".");
4260 type = namespaces.shift();
4263 ontype = type.indexOf(":") < 0 && "on" + type;
4265 // Caller can pass in a jQuery.Event object, Object, or just an event type string
4266 event = event[ jQuery.expando ] ?
4268 new jQuery.Event( type, typeof event === "object" && event );
4270 // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
4271 event.isTrigger = onlyHandlers ? 2 : 3;
4272 event.namespace = namespaces.join(".");
4273 event.namespace_re = event.namespace ?
4274 new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
4277 // Clean up the event in case it is being reused
4278 event.result = undefined;
4279 if ( !event.target ) {
4280 event.target = elem;
4283 // Clone any incoming data and prepend the event, creating the handler arg list
4284 data = data == null ?
4286 jQuery.makeArray( data, [ event ] );
4288 // Allow special events to draw outside the lines
4289 special = jQuery.event.special[ type ] || {};
4290 if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
4294 // Determine event propagation path in advance, per W3C events spec (#9951)
4295 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
4296 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
4298 bubbleType = special.delegateType || type;
4299 if ( !rfocusMorph.test( bubbleType + type ) ) {
4300 cur = cur.parentNode;
4302 for ( ; cur; cur = cur.parentNode ) {
4303 eventPath.push( cur );
4307 // Only add window if we got to document (e.g., not plain obj or detached DOM)
4308 if ( tmp === (elem.ownerDocument || document) ) {
4309 eventPath.push( tmp.defaultView || tmp.parentWindow || window );
4313 // Fire handlers on the event path
4315 while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
4317 event.type = i > 1 ?
4319 special.bindType || type;
4322 handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
4324 handle.apply( cur, data );
4328 handle = ontype && cur[ ontype ];
4329 if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
4330 event.result = handle.apply( cur, data );
4331 if ( event.result === false ) {
4332 event.preventDefault();
4338 // If nobody prevented the default action, do it now
4339 if ( !onlyHandlers && !event.isDefaultPrevented() ) {
4341 if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
4342 jQuery.acceptData( elem ) ) {
4344 // Call a native DOM method on the target with the same name name as the event.
4345 // Don't do default actions on window, that's where global variables be (#6170)
4346 if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
4348 // Don't re-trigger an onFOO event when we call its FOO() method
4349 tmp = elem[ ontype ];
4352 elem[ ontype ] = null;
4355 // Prevent re-triggering of the same event, since we already bubbled it above
4356 jQuery.event.triggered = type;
4358 jQuery.event.triggered = undefined;
4361 elem[ ontype ] = tmp;
4367 return event.result;
4370 dispatch: function( event ) {
4372 // Make a writable jQuery.Event from the native event object
4373 event = jQuery.event.fix( event );
4375 var i, j, ret, matched, handleObj,
4377 args = slice.call( arguments ),
4378 handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
4379 special = jQuery.event.special[ event.type ] || {};
4381 // Use the fix-ed jQuery.Event rather than the (read-only) native event
4383 event.delegateTarget = this;
4385 // Call the preDispatch hook for the mapped type, and let it bail if desired
4386 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
4390 // Determine handlers
4391 handlerQueue = jQuery.event.handlers.call( this, event, handlers );
4393 // Run delegates first; they may want to stop propagation beneath us
4395 while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
4396 event.currentTarget = matched.elem;
4399 while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
4401 // Triggered event must either 1) have no namespace, or
4402 // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
4403 if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
4405 event.handleObj = handleObj;
4406 event.data = handleObj.data;
4408 ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
4409 .apply( matched.elem, args );
4411 if ( ret !== undefined ) {
4412 if ( (event.result = ret) === false ) {
4413 event.preventDefault();
4414 event.stopPropagation();
4421 // Call the postDispatch hook for the mapped type
4422 if ( special.postDispatch ) {
4423 special.postDispatch.call( this, event );
4426 return event.result;
4429 handlers: function( event, handlers ) {
4430 var i, matches, sel, handleObj,
4432 delegateCount = handlers.delegateCount,
4435 // Find delegate handlers
4436 // Black-hole SVG <use> instance trees (#13180)
4437 // Avoid non-left-click bubbling in Firefox (#3861)
4438 if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
4440 for ( ; cur !== this; cur = cur.parentNode || this ) {
4442 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
4443 if ( cur.disabled !== true || event.type !== "click" ) {
4445 for ( i = 0; i < delegateCount; i++ ) {
4446 handleObj = handlers[ i ];
4448 // Don't conflict with Object.prototype properties (#13203)
4449 sel = handleObj.selector + " ";
4451 if ( matches[ sel ] === undefined ) {
4452 matches[ sel ] = handleObj.needsContext ?
4453 jQuery( sel, this ).index( cur ) >= 0 :
4454 jQuery.find( sel, this, null, [ cur ] ).length;
4456 if ( matches[ sel ] ) {
4457 matches.push( handleObj );
4460 if ( matches.length ) {
4461 handlerQueue.push({ elem: cur, handlers: matches });
4467 // Add the remaining (directly-bound) handlers
4468 if ( delegateCount < handlers.length ) {
4469 handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
4472 return handlerQueue;
4475 // Includes some event props shared by KeyEvent and MouseEvent
4476 props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
4481 props: "char charCode key keyCode".split(" "),
4482 filter: function( event, original ) {
4484 // Add which for key events
4485 if ( event.which == null ) {
4486 event.which = original.charCode != null ? original.charCode : original.keyCode;
4494 props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
4495 filter: function( event, original ) {
4496 var eventDoc, doc, body,
4497 button = original.button;
4499 // Calculate pageX/Y if missing and clientX/Y available
4500 if ( event.pageX == null && original.clientX != null ) {
4501 eventDoc = event.target.ownerDocument || document;
4502 doc = eventDoc.documentElement;
4503 body = eventDoc.body;
4505 event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
4506 event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
4509 // Add which for click: 1 === left; 2 === middle; 3 === right
4510 // Note: button is not normalized, so don't use it
4511 if ( !event.which && button !== undefined ) {
4512 event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
4519 fix: function( event ) {
4520 if ( event[ jQuery.expando ] ) {
4524 // Create a writable copy of the event object and normalize some properties
4527 originalEvent = event,
4528 fixHook = this.fixHooks[ type ];
4531 this.fixHooks[ type ] = fixHook =
4532 rmouseEvent.test( type ) ? this.mouseHooks :
4533 rkeyEvent.test( type ) ? this.keyHooks :
4536 copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
4538 event = new jQuery.Event( originalEvent );
4543 event[ prop ] = originalEvent[ prop ];
4546 // Support: Cordova 2.5 (WebKit) (#13255)
4547 // All events should have a target; Cordova deviceready doesn't
4548 if ( !event.target ) {
4549 event.target = document;
4552 // Support: Safari 6.0+, Chrome < 28
4553 // Target should not be a text node (#504, #13143)
4554 if ( event.target.nodeType === 3 ) {
4555 event.target = event.target.parentNode;
4558 return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
4563 // Prevent triggered image.load events from bubbling to window.load
4567 // Fire native event if possible so blur/focus sequence is correct
4568 trigger: function() {
4569 if ( this !== safeActiveElement() && this.focus ) {
4574 delegateType: "focusin"
4577 trigger: function() {
4578 if ( this === safeActiveElement() && this.blur ) {
4583 delegateType: "focusout"
4586 // For checkbox, fire native event so checked state will be right
4587 trigger: function() {
4588 if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
4594 // For cross-browser consistency, don't fire native .click() on links
4595 _default: function( event ) {
4596 return jQuery.nodeName( event.target, "a" );
4601 postDispatch: function( event ) {
4603 // Support: Firefox 20+
4604 // Firefox doesn't alert if the returnValue field is not set.
4605 if ( event.result !== undefined && event.originalEvent ) {
4606 event.originalEvent.returnValue = event.result;
4612 simulate: function( type, elem, event, bubble ) {
4613 // Piggyback on a donor event to simulate a different one.
4614 // Fake originalEvent to avoid donor's stopPropagation, but if the
4615 // simulated event prevents default then we do the same on the donor.
4616 var e = jQuery.extend(
4626 jQuery.event.trigger( e, null, elem );
4628 jQuery.event.dispatch.call( elem, e );
4630 if ( e.isDefaultPrevented() ) {
4631 event.preventDefault();
4636 jQuery.removeEvent = function( elem, type, handle ) {
4637 if ( elem.removeEventListener ) {
4638 elem.removeEventListener( type, handle, false );
4642 jQuery.Event = function( src, props ) {
4643 // Allow instantiation without the 'new' keyword
4644 if ( !(this instanceof jQuery.Event) ) {
4645 return new jQuery.Event( src, props );
4649 if ( src && src.type ) {
4650 this.originalEvent = src;
4651 this.type = src.type;
4653 // Events bubbling up the document may have been marked as prevented
4654 // by a handler lower down the tree; reflect the correct value.
4655 this.isDefaultPrevented = src.defaultPrevented ||
4656 src.defaultPrevented === undefined &&
4657 // Support: Android < 4.0
4658 src.returnValue === false ?
4667 // Put explicitly provided properties onto the event object
4669 jQuery.extend( this, props );
4672 // Create a timestamp if incoming event doesn't have one
4673 this.timeStamp = src && src.timeStamp || jQuery.now();
4676 this[ jQuery.expando ] = true;
4679 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
4680 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
4681 jQuery.Event.prototype = {
4682 isDefaultPrevented: returnFalse,
4683 isPropagationStopped: returnFalse,
4684 isImmediatePropagationStopped: returnFalse,
4686 preventDefault: function() {
4687 var e = this.originalEvent;
4689 this.isDefaultPrevented = returnTrue;
4691 if ( e && e.preventDefault ) {
4695 stopPropagation: function() {
4696 var e = this.originalEvent;
4698 this.isPropagationStopped = returnTrue;
4700 if ( e && e.stopPropagation ) {
4701 e.stopPropagation();
4704 stopImmediatePropagation: function() {
4705 var e = this.originalEvent;
4707 this.isImmediatePropagationStopped = returnTrue;
4709 if ( e && e.stopImmediatePropagation ) {
4710 e.stopImmediatePropagation();
4713 this.stopPropagation();
4717 // Create mouseenter/leave events using mouseover/out and event-time checks
4718 // Support: Chrome 15+
4720 mouseenter: "mouseover",
4721 mouseleave: "mouseout",
4722 pointerenter: "pointerover",
4723 pointerleave: "pointerout"
4724 }, function( orig, fix ) {
4725 jQuery.event.special[ orig ] = {
4729 handle: function( event ) {
4732 related = event.relatedTarget,
4733 handleObj = event.handleObj;
4735 // For mousenter/leave call the handler if related is outside the target.
4736 // NB: No relatedTarget if the mouse left/entered the browser window
4737 if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
4738 event.type = handleObj.origType;
4739 ret = handleObj.handler.apply( this, arguments );
4747 // Create "bubbling" focus and blur events
4748 // Support: Firefox, Chrome, Safari
4749 if ( !support.focusinBubbles ) {
4750 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
4752 // Attach a single capturing handler on the document while someone wants focusin/focusout
4753 var handler = function( event ) {
4754 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
4757 jQuery.event.special[ fix ] = {
4759 var doc = this.ownerDocument || this,
4760 attaches = data_priv.access( doc, fix );
4763 doc.addEventListener( orig, handler, true );
4765 data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
4767 teardown: function() {
4768 var doc = this.ownerDocument || this,
4769 attaches = data_priv.access( doc, fix ) - 1;
4772 doc.removeEventListener( orig, handler, true );
4773 data_priv.remove( doc, fix );
4776 data_priv.access( doc, fix, attaches );
4785 on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
4788 // Types can be a map of types/handlers
4789 if ( typeof types === "object" ) {
4790 // ( types-Object, selector, data )
4791 if ( typeof selector !== "string" ) {
4792 // ( types-Object, data )
4793 data = data || selector;
4794 selector = undefined;
4796 for ( type in types ) {
4797 this.on( type, selector, data, types[ type ], one );
4802 if ( data == null && fn == null ) {
4805 data = selector = undefined;
4806 } else if ( fn == null ) {
4807 if ( typeof selector === "string" ) {
4808 // ( types, selector, fn )
4812 // ( types, data, fn )
4815 selector = undefined;
4818 if ( fn === false ) {
4826 fn = function( event ) {
4827 // Can use an empty set, since event contains the info
4828 jQuery().off( event );
4829 return origFn.apply( this, arguments );
4831 // Use same guid so caller can remove using origFn
4832 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
4834 return this.each( function() {
4835 jQuery.event.add( this, types, fn, data, selector );
4838 one: function( types, selector, data, fn ) {
4839 return this.on( types, selector, data, fn, 1 );
4841 off: function( types, selector, fn ) {
4842 var handleObj, type;
4843 if ( types && types.preventDefault && types.handleObj ) {
4844 // ( event ) dispatched jQuery.Event
4845 handleObj = types.handleObj;
4846 jQuery( types.delegateTarget ).off(
4847 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
4853 if ( typeof types === "object" ) {
4854 // ( types-object [, selector] )
4855 for ( type in types ) {
4856 this.off( type, selector, types[ type ] );
4860 if ( selector === false || typeof selector === "function" ) {
4863 selector = undefined;
4865 if ( fn === false ) {
4868 return this.each(function() {
4869 jQuery.event.remove( this, types, fn, selector );
4873 trigger: function( type, data ) {
4874 return this.each(function() {
4875 jQuery.event.trigger( type, data, this );
4878 triggerHandler: function( type, data ) {
4881 return jQuery.event.trigger( type, data, elem, true );
4888 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
4889 rtagName = /<([\w:]+)/,
4890 rhtml = /<|&#?\w+;/,
4891 rnoInnerhtml = /<(?:script|style|link)/i,
4892 // checked="checked" or checked
4893 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
4894 rscriptType = /^$|\/(?:java|ecma)script/i,
4895 rscriptTypeMasked = /^true\/(.*)/,
4896 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
4898 // We have to close these tags to support XHTML (#13200)
4902 option: [ 1, "<select multiple='multiple'>", "</select>" ],
4904 thead: [ 1, "<table>", "</table>" ],
4905 col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
4906 tr: [ 2, "<table><tbody>", "</tbody></table>" ],
4907 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
4909 _default: [ 0, "", "" ]
4913 wrapMap.optgroup = wrapMap.option;
4915 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
4916 wrapMap.th = wrapMap.td;
4918 // Support: 1.x compatibility
4919 // Manipulating tables requires a tbody
4920 function manipulationTarget( elem, content ) {
4921 return jQuery.nodeName( elem, "table" ) &&
4922 jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
4924 elem.getElementsByTagName("tbody")[0] ||
4925 elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
4929 // Replace/restore the type attribute of script elements for safe DOM manipulation
4930 function disableScript( elem ) {
4931 elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
4934 function restoreScript( elem ) {
4935 var match = rscriptTypeMasked.exec( elem.type );
4938 elem.type = match[ 1 ];
4940 elem.removeAttribute("type");
4946 // Mark scripts as having already been evaluated
4947 function setGlobalEval( elems, refElements ) {
4951 for ( ; i < l; i++ ) {
4953 elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
4958 function cloneCopyEvent( src, dest ) {
4959 var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
4961 if ( dest.nodeType !== 1 ) {
4965 // 1. Copy private data: events, handlers, etc.
4966 if ( data_priv.hasData( src ) ) {
4967 pdataOld = data_priv.access( src );
4968 pdataCur = data_priv.set( dest, pdataOld );
4969 events = pdataOld.events;
4972 delete pdataCur.handle;
4973 pdataCur.events = {};
4975 for ( type in events ) {
4976 for ( i = 0, l = events[ type ].length; i < l; i++ ) {
4977 jQuery.event.add( dest, type, events[ type ][ i ] );
4983 // 2. Copy user data
4984 if ( data_user.hasData( src ) ) {
4985 udataOld = data_user.access( src );
4986 udataCur = jQuery.extend( {}, udataOld );
4988 data_user.set( dest, udataCur );
4992 function getAll( context, tag ) {
4993 var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
4994 context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
4997 return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
4998 jQuery.merge( [ context ], ret ) :
5003 function fixInput( src, dest ) {
5004 var nodeName = dest.nodeName.toLowerCase();
5006 // Fails to persist the checked state of a cloned checkbox or radio button.
5007 if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
5008 dest.checked = src.checked;
5010 // Fails to return the selected option to the default selected state when cloning options
5011 } else if ( nodeName === "input" || nodeName === "textarea" ) {
5012 dest.defaultValue = src.defaultValue;
5017 clone: function( elem, dataAndEvents, deepDataAndEvents ) {
5018 var i, l, srcElements, destElements,
5019 clone = elem.cloneNode( true ),
5020 inPage = jQuery.contains( elem.ownerDocument, elem );
5023 // Fix Cloning issues
5024 if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
5025 !jQuery.isXMLDoc( elem ) ) {
5027 // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
5028 destElements = getAll( clone );
5029 srcElements = getAll( elem );
5031 for ( i = 0, l = srcElements.length; i < l; i++ ) {
5032 fixInput( srcElements[ i ], destElements[ i ] );
5036 // Copy the events from the original to the clone
5037 if ( dataAndEvents ) {
5038 if ( deepDataAndEvents ) {
5039 srcElements = srcElements || getAll( elem );
5040 destElements = destElements || getAll( clone );
5042 for ( i = 0, l = srcElements.length; i < l; i++ ) {
5043 cloneCopyEvent( srcElements[ i ], destElements[ i ] );
5046 cloneCopyEvent( elem, clone );
5050 // Preserve script evaluation history
5051 destElements = getAll( clone, "script" );
5052 if ( destElements.length > 0 ) {
5053 setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
5056 // Return the cloned set
5060 buildFragment: function( elems, context, scripts, selection ) {
5061 var elem, tmp, tag, wrap, contains, j,
5062 fragment = context.createDocumentFragment(),
5067 for ( ; i < l; i++ ) {
5070 if ( elem || elem === 0 ) {
5072 // Add nodes directly
5073 if ( jQuery.type( elem ) === "object" ) {
5074 // Support: QtWebKit
5075 // jQuery.merge because push.apply(_, arraylike) throws
5076 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
5078 // Convert non-html into a text node
5079 } else if ( !rhtml.test( elem ) ) {
5080 nodes.push( context.createTextNode( elem ) );
5082 // Convert html into DOM nodes
5084 tmp = tmp || fragment.appendChild( context.createElement("div") );
5086 // Deserialize a standard representation
5087 tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
5088 wrap = wrapMap[ tag ] || wrapMap._default;
5089 tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
5091 // Descend through wrappers to the right content
5094 tmp = tmp.lastChild;
5097 // Support: QtWebKit
5098 // jQuery.merge because push.apply(_, arraylike) throws
5099 jQuery.merge( nodes, tmp.childNodes );
5101 // Remember the top-level container
5102 tmp = fragment.firstChild;
5105 // Support: Webkit, IE
5106 tmp.textContent = "";
5111 // Remove wrapper from fragment
5112 fragment.textContent = "";
5115 while ( (elem = nodes[ i++ ]) ) {
5117 // #4087 - If origin and destination elements are the same, and this is
5118 // that element, do not do anything
5119 if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
5123 contains = jQuery.contains( elem.ownerDocument, elem );
5125 // Append to fragment
5126 tmp = getAll( fragment.appendChild( elem ), "script" );
5128 // Preserve script evaluation history
5130 setGlobalEval( tmp );
5133 // Capture executables
5136 while ( (elem = tmp[ j++ ]) ) {
5137 if ( rscriptType.test( elem.type || "" ) ) {
5138 scripts.push( elem );
5147 cleanData: function( elems ) {
5148 var data, elem, type, key,
5149 special = jQuery.event.special,
5152 for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
5153 if ( jQuery.acceptData( elem ) ) {
5154 key = elem[ data_priv.expando ];
5156 if ( key && (data = data_priv.cache[ key ]) ) {
5157 if ( data.events ) {
5158 for ( type in data.events ) {
5159 if ( special[ type ] ) {
5160 jQuery.event.remove( elem, type );
5162 // This is a shortcut to avoid jQuery.event.remove's overhead
5164 jQuery.removeEvent( elem, type, data.handle );
5168 if ( data_priv.cache[ key ] ) {
5169 // Discard any remaining `private` data
5170 delete data_priv.cache[ key ];
5174 // Discard any remaining `user` data
5175 delete data_user.cache[ elem[ data_user.expando ] ];
5181 text: function( value ) {
5182 return access( this, function( value ) {
5183 return value === undefined ?
5184 jQuery.text( this ) :
5185 this.empty().each(function() {
5186 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5187 this.textContent = value;
5190 }, null, value, arguments.length );
5193 append: function() {
5194 return this.domManip( arguments, function( elem ) {
5195 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5196 var target = manipulationTarget( this, elem );
5197 target.appendChild( elem );
5202 prepend: function() {
5203 return this.domManip( arguments, function( elem ) {
5204 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5205 var target = manipulationTarget( this, elem );
5206 target.insertBefore( elem, target.firstChild );
5211 before: function() {
5212 return this.domManip( arguments, function( elem ) {
5213 if ( this.parentNode ) {
5214 this.parentNode.insertBefore( elem, this );
5220 return this.domManip( arguments, function( elem ) {
5221 if ( this.parentNode ) {
5222 this.parentNode.insertBefore( elem, this.nextSibling );
5227 remove: function( selector, keepData /* Internal Use Only */ ) {
5229 elems = selector ? jQuery.filter( selector, this ) : this,
5232 for ( ; (elem = elems[i]) != null; i++ ) {
5233 if ( !keepData && elem.nodeType === 1 ) {
5234 jQuery.cleanData( getAll( elem ) );
5237 if ( elem.parentNode ) {
5238 if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
5239 setGlobalEval( getAll( elem, "script" ) );
5241 elem.parentNode.removeChild( elem );
5252 for ( ; (elem = this[i]) != null; i++ ) {
5253 if ( elem.nodeType === 1 ) {
5255 // Prevent memory leaks
5256 jQuery.cleanData( getAll( elem, false ) );
5258 // Remove any remaining nodes
5259 elem.textContent = "";
5266 clone: function( dataAndEvents, deepDataAndEvents ) {
5267 dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
5268 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
5270 return this.map(function() {
5271 return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
5275 html: function( value ) {
5276 return access( this, function( value ) {
5277 var elem = this[ 0 ] || {},
5281 if ( value === undefined && elem.nodeType === 1 ) {
5282 return elem.innerHTML;
5285 // See if we can take a shortcut and just use innerHTML
5286 if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
5287 !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
5289 value = value.replace( rxhtmlTag, "<$1></$2>" );
5292 for ( ; i < l; i++ ) {
5293 elem = this[ i ] || {};
5295 // Remove element nodes and prevent memory leaks
5296 if ( elem.nodeType === 1 ) {
5297 jQuery.cleanData( getAll( elem, false ) );
5298 elem.innerHTML = value;
5304 // If using innerHTML throws an exception, use the fallback method
5309 this.empty().append( value );
5311 }, null, value, arguments.length );
5314 replaceWith: function() {
5315 var arg = arguments[ 0 ];
5317 // Make the changes, replacing each context element with the new content
5318 this.domManip( arguments, function( elem ) {
5319 arg = this.parentNode;
5321 jQuery.cleanData( getAll( this ) );
5324 arg.replaceChild( elem, this );
5328 // Force removal if there was no new content (e.g., from empty arguments)
5329 return arg && (arg.length || arg.nodeType) ? this : this.remove();
5332 detach: function( selector ) {
5333 return this.remove( selector, true );
5336 domManip: function( args, callback ) {
5338 // Flatten any nested arrays
5339 args = concat.apply( [], args );
5341 var fragment, first, scripts, hasScripts, node, doc,
5347 isFunction = jQuery.isFunction( value );
5349 // We can't cloneNode fragments that contain checked, in WebKit
5351 ( l > 1 && typeof value === "string" &&
5352 !support.checkClone && rchecked.test( value ) ) ) {
5353 return this.each(function( index ) {
5354 var self = set.eq( index );
5356 args[ 0 ] = value.call( this, index, self.html() );
5358 self.domManip( args, callback );
5363 fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
5364 first = fragment.firstChild;
5366 if ( fragment.childNodes.length === 1 ) {
5371 scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
5372 hasScripts = scripts.length;
5374 // Use the original fragment for the last item instead of the first because it can end up
5375 // being emptied incorrectly in certain situations (#8070).
5376 for ( ; i < l; i++ ) {
5379 if ( i !== iNoClone ) {
5380 node = jQuery.clone( node, true, true );
5382 // Keep references to cloned scripts for later restoration
5384 // Support: QtWebKit
5385 // jQuery.merge because push.apply(_, arraylike) throws
5386 jQuery.merge( scripts, getAll( node, "script" ) );
5390 callback.call( this[ i ], node, i );
5394 doc = scripts[ scripts.length - 1 ].ownerDocument;
5397 jQuery.map( scripts, restoreScript );
5399 // Evaluate executable scripts on first document insertion
5400 for ( i = 0; i < hasScripts; i++ ) {
5401 node = scripts[ i ];
5402 if ( rscriptType.test( node.type || "" ) &&
5403 !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
5406 // Optional AJAX dependency, but won't run scripts if not present
5407 if ( jQuery._evalUrl ) {
5408 jQuery._evalUrl( node.src );
5411 jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
5425 prependTo: "prepend",
5426 insertBefore: "before",
5427 insertAfter: "after",
5428 replaceAll: "replaceWith"
5429 }, function( name, original ) {
5430 jQuery.fn[ name ] = function( selector ) {
5433 insert = jQuery( selector ),
5434 last = insert.length - 1,
5437 for ( ; i <= last; i++ ) {
5438 elems = i === last ? this : this.clone( true );
5439 jQuery( insert[ i ] )[ original ]( elems );
5441 // Support: QtWebKit
5442 // .get() because push.apply(_, arraylike) throws
5443 push.apply( ret, elems.get() );
5446 return this.pushStack( ret );
5455 * Retrieve the actual display of a element
5456 * @param {String} name nodeName of the element
5457 * @param {Object} doc Document object
5459 // Called only from within defaultDisplay
5460 function actualDisplay( name, doc ) {
5462 elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
5464 // getDefaultComputedStyle might be reliably used only on attached element
5465 display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
5467 // Use of this method is a temporary fix (more like optmization) until something better comes along,
5468 // since it was removed from specification and supported only in FF
5469 style.display : jQuery.css( elem[ 0 ], "display" );
5471 // We don't have any data stored on the element,
5472 // so use "detach" method as fast way to get rid of the element
5479 * Try to determine the default display value of an element
5480 * @param {String} nodeName
5482 function defaultDisplay( nodeName ) {
5484 display = elemdisplay[ nodeName ];
5487 display = actualDisplay( nodeName, doc );
5489 // If the simple way fails, read from inside an iframe
5490 if ( display === "none" || !display ) {
5492 // Use the already-created iframe if possible
5493 iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
5495 // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
5496 doc = iframe[ 0 ].contentDocument;
5502 display = actualDisplay( nodeName, doc );
5506 // Store the correct default display
5507 elemdisplay[ nodeName ] = display;
5512 var rmargin = (/^margin/);
5514 var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
5516 var getStyles = function( elem ) {
5517 return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
5522 function curCSS( elem, name, computed ) {
5523 var width, minWidth, maxWidth, ret,
5526 computed = computed || getStyles( elem );
5529 // getPropertyValue is only needed for .css('filter') in IE9, see #12537
5531 ret = computed.getPropertyValue( name ) || computed[ name ];
5536 if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
5537 ret = jQuery.style( elem, name );
5541 // A tribute to the "awesome hack by Dean Edwards"
5542 // iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
5543 // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
5544 if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
5546 // Remember the original values
5547 width = style.width;
5548 minWidth = style.minWidth;
5549 maxWidth = style.maxWidth;
5551 // Put in the new values to get a computed value out
5552 style.minWidth = style.maxWidth = style.width = ret;
5553 ret = computed.width;
5555 // Revert the changed values
5556 style.width = width;
5557 style.minWidth = minWidth;
5558 style.maxWidth = maxWidth;
5562 return ret !== undefined ?
5564 // IE returns zIndex value as an integer.
5570 function addGetHookIf( conditionFn, hookFn ) {
5571 // Define the hook, we'll check on the first run if it's really needed.
5574 if ( conditionFn() ) {
5575 // Hook not needed (or it's not possible to use it due to missing dependency),
5577 // Since there are no other hooks for marginRight, remove the whole object.
5582 // Hook needed; redefine it so that the support test is not executed again.
5584 return (this.get = hookFn).apply( this, arguments );
5591 var pixelPositionVal, boxSizingReliableVal,
5592 docElem = document.documentElement,
5593 container = document.createElement( "div" ),
5594 div = document.createElement( "div" );
5600 div.style.backgroundClip = "content-box";
5601 div.cloneNode( true ).style.backgroundClip = "";
5602 support.clearCloneStyle = div.style.backgroundClip === "content-box";
5604 container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" +
5605 "position:absolute";
5606 container.appendChild( div );
5608 // Executing both pixelPosition & boxSizingReliable tests require only one layout
5609 // so they're executed at the same time to save the second computation.
5610 function computePixelPositionAndBoxSizingReliable() {
5612 // Support: Firefox<29, Android 2.3
5613 // Vendor-prefix box-sizing
5614 "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
5615 "box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
5616 "border:1px;padding:1px;width:4px;position:absolute";
5618 docElem.appendChild( container );
5620 var divStyle = window.getComputedStyle( div, null );
5621 pixelPositionVal = divStyle.top !== "1%";
5622 boxSizingReliableVal = divStyle.width === "4px";
5624 docElem.removeChild( container );
5627 // Support: node.js jsdom
5628 // Don't assume that getComputedStyle is a property of the global object
5629 if ( window.getComputedStyle ) {
5630 jQuery.extend( support, {
5631 pixelPosition: function() {
5632 // This test is executed only once but we still do memoizing
5633 // since we can use the boxSizingReliable pre-computing.
5634 // No need to check if the test was already performed, though.
5635 computePixelPositionAndBoxSizingReliable();
5636 return pixelPositionVal;
5638 boxSizingReliable: function() {
5639 if ( boxSizingReliableVal == null ) {
5640 computePixelPositionAndBoxSizingReliable();
5642 return boxSizingReliableVal;
5644 reliableMarginRight: function() {
5645 // Support: Android 2.3
5646 // Check if div with explicit width and no margin-right incorrectly
5647 // gets computed margin-right based on width of container. (#3333)
5648 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
5649 // This support function is only executed once so no memoizing is needed.
5651 marginDiv = div.appendChild( document.createElement( "div" ) );
5653 // Reset CSS: box-sizing; display; margin; border; padding
5654 marginDiv.style.cssText = div.style.cssText =
5655 // Support: Firefox<29, Android 2.3
5656 // Vendor-prefix box-sizing
5657 "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
5658 "box-sizing:content-box;display:block;margin:0;border:0;padding:0";
5659 marginDiv.style.marginRight = marginDiv.style.width = "0";
5660 div.style.width = "1px";
5661 docElem.appendChild( container );
5663 ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight );
5665 docElem.removeChild( container );
5674 // A method for quickly swapping in/out CSS properties to get correct calculations.
5675 jQuery.swap = function( elem, options, callback, args ) {
5679 // Remember the old values, and insert the new ones
5680 for ( name in options ) {
5681 old[ name ] = elem.style[ name ];
5682 elem.style[ name ] = options[ name ];
5685 ret = callback.apply( elem, args || [] );
5687 // Revert the old values
5688 for ( name in options ) {
5689 elem.style[ name ] = old[ name ];
5697 // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
5698 // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
5699 rdisplayswap = /^(none|table(?!-c[ea]).+)/,
5700 rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
5701 rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
5703 cssShow = { position: "absolute", visibility: "hidden", display: "block" },
5704 cssNormalTransform = {
5709 cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
5711 // return a css property mapped to a potentially vendor prefixed property
5712 function vendorPropName( style, name ) {
5714 // shortcut for names that are not vendor prefixed
5715 if ( name in style ) {
5719 // check for vendor prefixed names
5720 var capName = name[0].toUpperCase() + name.slice(1),
5722 i = cssPrefixes.length;
5725 name = cssPrefixes[ i ] + capName;
5726 if ( name in style ) {
5734 function setPositiveNumber( elem, value, subtract ) {
5735 var matches = rnumsplit.exec( value );
5737 // Guard against undefined "subtract", e.g., when used as in cssHooks
5738 Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
5742 function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
5743 var i = extra === ( isBorderBox ? "border" : "content" ) ?
5744 // If we already have the right measurement, avoid augmentation
5746 // Otherwise initialize for horizontal or vertical properties
5747 name === "width" ? 1 : 0,
5751 for ( ; i < 4; i += 2 ) {
5752 // both box models exclude margin, so add it if we want it
5753 if ( extra === "margin" ) {
5754 val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
5757 if ( isBorderBox ) {
5758 // border-box includes padding, so remove it if we want content
5759 if ( extra === "content" ) {
5760 val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
5763 // at this point, extra isn't border nor margin, so remove border
5764 if ( extra !== "margin" ) {
5765 val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
5768 // at this point, extra isn't content, so add padding
5769 val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
5771 // at this point, extra isn't content nor padding, so add border
5772 if ( extra !== "padding" ) {
5773 val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
5781 function getWidthOrHeight( elem, name, extra ) {
5783 // Start with offset property, which is equivalent to the border-box value
5784 var valueIsBorderBox = true,
5785 val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
5786 styles = getStyles( elem ),
5787 isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
5789 // some non-html elements return undefined for offsetWidth, so check for null/undefined
5790 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
5791 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
5792 if ( val <= 0 || val == null ) {
5793 // Fall back to computed then uncomputed css if necessary
5794 val = curCSS( elem, name, styles );
5795 if ( val < 0 || val == null ) {
5796 val = elem.style[ name ];
5799 // Computed unit is not pixels. Stop here and return.
5800 if ( rnumnonpx.test(val) ) {
5804 // we need the check for style in case a browser which returns unreliable values
5805 // for getComputedStyle silently falls back to the reliable elem.style
5806 valueIsBorderBox = isBorderBox &&
5807 ( support.boxSizingReliable() || val === elem.style[ name ] );
5809 // Normalize "", auto, and prepare for extra
5810 val = parseFloat( val ) || 0;
5813 // use the active box-sizing model to add/subtract irrelevant styles
5815 augmentWidthOrHeight(
5818 extra || ( isBorderBox ? "border" : "content" ),
5825 function showHide( elements, show ) {
5826 var display, elem, hidden,
5829 length = elements.length;
5831 for ( ; index < length; index++ ) {
5832 elem = elements[ index ];
5833 if ( !elem.style ) {
5837 values[ index ] = data_priv.get( elem, "olddisplay" );
5838 display = elem.style.display;
5840 // Reset the inline display of this element to learn if it is
5841 // being hidden by cascaded rules or not
5842 if ( !values[ index ] && display === "none" ) {
5843 elem.style.display = "";
5846 // Set elements which have been overridden with display: none
5847 // in a stylesheet to whatever the default browser style is
5848 // for such an element
5849 if ( elem.style.display === "" && isHidden( elem ) ) {
5850 values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) );
5853 hidden = isHidden( elem );
5855 if ( display !== "none" || !hidden ) {
5856 data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
5861 // Set the display of most of the elements in a second loop
5862 // to avoid the constant reflow
5863 for ( index = 0; index < length; index++ ) {
5864 elem = elements[ index ];
5865 if ( !elem.style ) {
5868 if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
5869 elem.style.display = show ? values[ index ] || "" : "none";
5877 // Add in style property hooks for overriding the default
5878 // behavior of getting and setting a style property
5881 get: function( elem, computed ) {
5883 // We should always get a number back from opacity
5884 var ret = curCSS( elem, "opacity" );
5885 return ret === "" ? "1" : ret;
5891 // Don't automatically add "px" to these possibly-unitless properties
5893 "columnCount": true,
5894 "fillOpacity": true,
5907 // Add in properties whose names you wish to fix before
5908 // setting or getting the value
5910 // normalize float css property
5914 // Get and set the style property on a DOM Node
5915 style: function( elem, name, value, extra ) {
5916 // Don't set styles on text and comment nodes
5917 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
5921 // Make sure that we're working with the right name
5922 var ret, type, hooks,
5923 origName = jQuery.camelCase( name ),
5926 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
5928 // gets hook for the prefixed version
5929 // followed by the unprefixed version
5930 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
5932 // Check if we're setting a value
5933 if ( value !== undefined ) {
5934 type = typeof value;
5936 // convert relative number strings (+= or -=) to relative numbers. #7345
5937 if ( type === "string" && (ret = rrelNum.exec( value )) ) {
5938 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
5943 // Make sure that null and NaN values aren't set. See: #7116
5944 if ( value == null || value !== value ) {
5948 // If a number was passed in, add 'px' to the (except for certain CSS properties)
5949 if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
5953 // Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
5954 // but it would mean to define eight (for every problematic property) identical functions
5955 if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
5956 style[ name ] = "inherit";
5959 // If a hook was provided, use that value, otherwise just set the specified value
5960 if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
5961 style[ name ] = value;
5965 // If a hook was provided get the non-computed value from there
5966 if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
5970 // Otherwise just get the value from the style object
5971 return style[ name ];
5975 css: function( elem, name, extra, styles ) {
5976 var val, num, hooks,
5977 origName = jQuery.camelCase( name );
5979 // Make sure that we're working with the right name
5980 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
5982 // gets hook for the prefixed version
5983 // followed by the unprefixed version
5984 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
5986 // If a hook was provided get the computed value from there
5987 if ( hooks && "get" in hooks ) {
5988 val = hooks.get( elem, true, extra );
5991 // Otherwise, if a way to get the computed value exists, use that
5992 if ( val === undefined ) {
5993 val = curCSS( elem, name, styles );
5996 //convert "normal" to computed value
5997 if ( val === "normal" && name in cssNormalTransform ) {
5998 val = cssNormalTransform[ name ];
6001 // Return, converting to number if forced or a qualifier was provided and val looks numeric
6002 if ( extra === "" || extra ) {
6003 num = parseFloat( val );
6004 return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
6010 jQuery.each([ "height", "width" ], function( i, name ) {
6011 jQuery.cssHooks[ name ] = {
6012 get: function( elem, computed, extra ) {
6014 // certain elements can have dimension info if we invisibly show them
6015 // however, it must have a current display style that would benefit from this
6016 return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
6017 jQuery.swap( elem, cssShow, function() {
6018 return getWidthOrHeight( elem, name, extra );
6020 getWidthOrHeight( elem, name, extra );
6024 set: function( elem, value, extra ) {
6025 var styles = extra && getStyles( elem );
6026 return setPositiveNumber( elem, value, extra ?
6027 augmentWidthOrHeight(
6031 jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
6039 // Support: Android 2.3
6040 jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
6041 function( elem, computed ) {
6043 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
6044 // Work around by temporarily setting element display to inline-block
6045 return jQuery.swap( elem, { "display": "inline-block" },
6046 curCSS, [ elem, "marginRight" ] );
6051 // These hooks are used by animate to expand properties
6056 }, function( prefix, suffix ) {
6057 jQuery.cssHooks[ prefix + suffix ] = {
6058 expand: function( value ) {
6062 // assumes a single number if not a string
6063 parts = typeof value === "string" ? value.split(" ") : [ value ];
6065 for ( ; i < 4; i++ ) {
6066 expanded[ prefix + cssExpand[ i ] + suffix ] =
6067 parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
6074 if ( !rmargin.test( prefix ) ) {
6075 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
6080 css: function( name, value ) {
6081 return access( this, function( elem, name, value ) {
6086 if ( jQuery.isArray( name ) ) {
6087 styles = getStyles( elem );
6090 for ( ; i < len; i++ ) {
6091 map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
6097 return value !== undefined ?
6098 jQuery.style( elem, name, value ) :
6099 jQuery.css( elem, name );
6100 }, name, value, arguments.length > 1 );
6103 return showHide( this, true );
6106 return showHide( this );
6108 toggle: function( state ) {
6109 if ( typeof state === "boolean" ) {
6110 return state ? this.show() : this.hide();
6113 return this.each(function() {
6114 if ( isHidden( this ) ) {
6115 jQuery( this ).show();
6117 jQuery( this ).hide();
6124 function Tween( elem, options, prop, end, easing ) {
6125 return new Tween.prototype.init( elem, options, prop, end, easing );
6127 jQuery.Tween = Tween;
6131 init: function( elem, options, prop, end, easing, unit ) {
6134 this.easing = easing || "swing";
6135 this.options = options;
6136 this.start = this.now = this.cur();
6138 this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
6141 var hooks = Tween.propHooks[ this.prop ];
6143 return hooks && hooks.get ?
6145 Tween.propHooks._default.get( this );
6147 run: function( percent ) {
6149 hooks = Tween.propHooks[ this.prop ];
6151 if ( this.options.duration ) {
6152 this.pos = eased = jQuery.easing[ this.easing ](
6153 percent, this.options.duration * percent, 0, 1, this.options.duration
6156 this.pos = eased = percent;
6158 this.now = ( this.end - this.start ) * eased + this.start;
6160 if ( this.options.step ) {
6161 this.options.step.call( this.elem, this.now, this );
6164 if ( hooks && hooks.set ) {
6167 Tween.propHooks._default.set( this );
6173 Tween.prototype.init.prototype = Tween.prototype;
6177 get: function( tween ) {
6180 if ( tween.elem[ tween.prop ] != null &&
6181 (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
6182 return tween.elem[ tween.prop ];
6185 // passing an empty string as a 3rd parameter to .css will automatically
6186 // attempt a parseFloat and fallback to a string if the parse fails
6187 // so, simple values such as "10px" are parsed to Float.
6188 // complex values such as "rotate(1rad)" are returned as is.
6189 result = jQuery.css( tween.elem, tween.prop, "" );
6190 // Empty strings, null, undefined and "auto" are converted to 0.
6191 return !result || result === "auto" ? 0 : result;
6193 set: function( tween ) {
6194 // use step hook for back compat - use cssHook if its there - use .style if its
6195 // available and use plain properties where available
6196 if ( jQuery.fx.step[ tween.prop ] ) {
6197 jQuery.fx.step[ tween.prop ]( tween );
6198 } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
6199 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
6201 tween.elem[ tween.prop ] = tween.now;
6208 // Panic based approach to setting things on disconnected nodes
6210 Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
6211 set: function( tween ) {
6212 if ( tween.elem.nodeType && tween.elem.parentNode ) {
6213 tween.elem[ tween.prop ] = tween.now;
6219 linear: function( p ) {
6222 swing: function( p ) {
6223 return 0.5 - Math.cos( p * Math.PI ) / 2;
6227 jQuery.fx = Tween.prototype.init;
6229 // Back Compat <1.8 extension point
6230 jQuery.fx.step = {};
6237 rfxtypes = /^(?:toggle|show|hide)$/,
6238 rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
6239 rrun = /queueHooks$/,
6240 animationPrefilters = [ defaultPrefilter ],
6242 "*": [ function( prop, value ) {
6243 var tween = this.createTween( prop, value ),
6244 target = tween.cur(),
6245 parts = rfxnum.exec( value ),
6246 unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
6248 // Starting value computation is required for potential unit mismatches
6249 start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
6250 rfxnum.exec( jQuery.css( tween.elem, prop ) ),
6254 if ( start && start[ 3 ] !== unit ) {
6255 // Trust units reported by jQuery.css
6256 unit = unit || start[ 3 ];
6258 // Make sure we update the tween properties later on
6259 parts = parts || [];
6261 // Iteratively approximate from a nonzero starting point
6262 start = +target || 1;
6265 // If previous iteration zeroed out, double until we get *something*
6266 // Use a string for doubling factor so we don't accidentally see scale as unchanged below
6267 scale = scale || ".5";
6270 start = start / scale;
6271 jQuery.style( tween.elem, prop, start + unit );
6273 // Update scale, tolerating zero or NaN from tween.cur()
6274 // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
6275 } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
6278 // Update tween properties
6280 start = tween.start = +start || +target || 0;
6282 // If a +=/-= token was provided, we're doing a relative animation
6283 tween.end = parts[ 1 ] ?
6284 start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
6292 // Animations created synchronously will run synchronously
6293 function createFxNow() {
6294 setTimeout(function() {
6297 return ( fxNow = jQuery.now() );
6300 // Generate parameters to create a standard animation
6301 function genFx( type, includeWidth ) {
6304 attrs = { height: type };
6306 // if we include width, step value is 1 to do all cssExpand values,
6307 // if we don't include width, step value is 2 to skip over Left and Right
6308 includeWidth = includeWidth ? 1 : 0;
6309 for ( ; i < 4 ; i += 2 - includeWidth ) {
6310 which = cssExpand[ i ];
6311 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
6314 if ( includeWidth ) {
6315 attrs.opacity = attrs.width = type;
6321 function createTween( value, prop, animation ) {
6323 collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
6325 length = collection.length;
6326 for ( ; index < length; index++ ) {
6327 if ( (tween = collection[ index ].call( animation, prop, value )) ) {
6329 // we're done with this property
6335 function defaultPrefilter( elem, props, opts ) {
6336 /* jshint validthis: true */
6337 var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
6341 hidden = elem.nodeType && isHidden( elem ),
6342 dataShow = data_priv.get( elem, "fxshow" );
6344 // handle queue: false promises
6345 if ( !opts.queue ) {
6346 hooks = jQuery._queueHooks( elem, "fx" );
6347 if ( hooks.unqueued == null ) {
6349 oldfire = hooks.empty.fire;
6350 hooks.empty.fire = function() {
6351 if ( !hooks.unqueued ) {
6358 anim.always(function() {
6359 // doing this makes sure that the complete handler will be called
6360 // before this completes
6361 anim.always(function() {
6363 if ( !jQuery.queue( elem, "fx" ).length ) {
6370 // height/width overflow pass
6371 if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
6372 // Make sure that nothing sneaks out
6373 // Record all 3 overflow attributes because IE9-10 do not
6374 // change the overflow attribute when overflowX and
6375 // overflowY are set to the same value
6376 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
6378 // Set display property to inline-block for height/width
6379 // animations on inline elements that are having width/height animated
6380 display = jQuery.css( elem, "display" );
6382 // Test default display if display is currently "none"
6383 checkDisplay = display === "none" ?
6384 data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
6386 if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
6387 style.display = "inline-block";
6391 if ( opts.overflow ) {
6392 style.overflow = "hidden";
6393 anim.always(function() {
6394 style.overflow = opts.overflow[ 0 ];
6395 style.overflowX = opts.overflow[ 1 ];
6396 style.overflowY = opts.overflow[ 2 ];
6401 for ( prop in props ) {
6402 value = props[ prop ];
6403 if ( rfxtypes.exec( value ) ) {
6404 delete props[ prop ];
6405 toggle = toggle || value === "toggle";
6406 if ( value === ( hidden ? "hide" : "show" ) ) {
6408 // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
6409 if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
6415 orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
6417 // Any non-fx value stops us from restoring the original display value
6419 display = undefined;
6423 if ( !jQuery.isEmptyObject( orig ) ) {
6425 if ( "hidden" in dataShow ) {
6426 hidden = dataShow.hidden;
6429 dataShow = data_priv.access( elem, "fxshow", {} );
6432 // store state if its toggle - enables .stop().toggle() to "reverse"
6434 dataShow.hidden = !hidden;
6437 jQuery( elem ).show();
6439 anim.done(function() {
6440 jQuery( elem ).hide();
6443 anim.done(function() {
6446 data_priv.remove( elem, "fxshow" );
6447 for ( prop in orig ) {
6448 jQuery.style( elem, prop, orig[ prop ] );
6451 for ( prop in orig ) {
6452 tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
6454 if ( !( prop in dataShow ) ) {
6455 dataShow[ prop ] = tween.start;
6457 tween.end = tween.start;
6458 tween.start = prop === "width" || prop === "height" ? 1 : 0;
6463 // If this is a noop like .hide().hide(), restore an overwritten display value
6464 } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
6465 style.display = display;
6469 function propFilter( props, specialEasing ) {
6470 var index, name, easing, value, hooks;
6472 // camelCase, specialEasing and expand cssHook pass
6473 for ( index in props ) {
6474 name = jQuery.camelCase( index );
6475 easing = specialEasing[ name ];
6476 value = props[ index ];
6477 if ( jQuery.isArray( value ) ) {
6478 easing = value[ 1 ];
6479 value = props[ index ] = value[ 0 ];
6482 if ( index !== name ) {
6483 props[ name ] = value;
6484 delete props[ index ];
6487 hooks = jQuery.cssHooks[ name ];
6488 if ( hooks && "expand" in hooks ) {
6489 value = hooks.expand( value );
6490 delete props[ name ];
6492 // not quite $.extend, this wont overwrite keys already present.
6493 // also - reusing 'index' from above because we have the correct "name"
6494 for ( index in value ) {
6495 if ( !( index in props ) ) {
6496 props[ index ] = value[ index ];
6497 specialEasing[ index ] = easing;
6501 specialEasing[ name ] = easing;
6506 function Animation( elem, properties, options ) {
6510 length = animationPrefilters.length,
6511 deferred = jQuery.Deferred().always( function() {
6512 // don't match elem in the :animated selector
6519 var currentTime = fxNow || createFxNow(),
6520 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
6521 // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
6522 temp = remaining / animation.duration || 0,
6525 length = animation.tweens.length;
6527 for ( ; index < length ; index++ ) {
6528 animation.tweens[ index ].run( percent );
6531 deferred.notifyWith( elem, [ animation, percent, remaining ]);
6533 if ( percent < 1 && length ) {
6536 deferred.resolveWith( elem, [ animation ] );
6540 animation = deferred.promise({
6542 props: jQuery.extend( {}, properties ),
6543 opts: jQuery.extend( true, { specialEasing: {} }, options ),
6544 originalProperties: properties,
6545 originalOptions: options,
6546 startTime: fxNow || createFxNow(),
6547 duration: options.duration,
6549 createTween: function( prop, end ) {
6550 var tween = jQuery.Tween( elem, animation.opts, prop, end,
6551 animation.opts.specialEasing[ prop ] || animation.opts.easing );
6552 animation.tweens.push( tween );
6555 stop: function( gotoEnd ) {
6557 // if we are going to the end, we want to run all the tweens
6558 // otherwise we skip this part
6559 length = gotoEnd ? animation.tweens.length : 0;
6564 for ( ; index < length ; index++ ) {
6565 animation.tweens[ index ].run( 1 );
6568 // resolve when we played the last frame
6569 // otherwise, reject
6571 deferred.resolveWith( elem, [ animation, gotoEnd ] );
6573 deferred.rejectWith( elem, [ animation, gotoEnd ] );
6578 props = animation.props;
6580 propFilter( props, animation.opts.specialEasing );
6582 for ( ; index < length ; index++ ) {
6583 result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
6589 jQuery.map( props, createTween, animation );
6591 if ( jQuery.isFunction( animation.opts.start ) ) {
6592 animation.opts.start.call( elem, animation );
6596 jQuery.extend( tick, {
6599 queue: animation.opts.queue
6603 // attach callbacks from options
6604 return animation.progress( animation.opts.progress )
6605 .done( animation.opts.done, animation.opts.complete )
6606 .fail( animation.opts.fail )
6607 .always( animation.opts.always );
6610 jQuery.Animation = jQuery.extend( Animation, {
6612 tweener: function( props, callback ) {
6613 if ( jQuery.isFunction( props ) ) {
6617 props = props.split(" ");
6622 length = props.length;
6624 for ( ; index < length ; index++ ) {
6625 prop = props[ index ];
6626 tweeners[ prop ] = tweeners[ prop ] || [];
6627 tweeners[ prop ].unshift( callback );
6631 prefilter: function( callback, prepend ) {
6633 animationPrefilters.unshift( callback );
6635 animationPrefilters.push( callback );
6640 jQuery.speed = function( speed, easing, fn ) {
6641 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
6642 complete: fn || !fn && easing ||
6643 jQuery.isFunction( speed ) && speed,
6645 easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
6648 opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
6649 opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
6651 // normalize opt.queue - true/undefined/null -> "fx"
6652 if ( opt.queue == null || opt.queue === true ) {
6657 opt.old = opt.complete;
6659 opt.complete = function() {
6660 if ( jQuery.isFunction( opt.old ) ) {
6661 opt.old.call( this );
6665 jQuery.dequeue( this, opt.queue );
6673 fadeTo: function( speed, to, easing, callback ) {
6675 // show any hidden elements after setting opacity to 0
6676 return this.filter( isHidden ).css( "opacity", 0 ).show()
6678 // animate to the value specified
6679 .end().animate({ opacity: to }, speed, easing, callback );
6681 animate: function( prop, speed, easing, callback ) {
6682 var empty = jQuery.isEmptyObject( prop ),
6683 optall = jQuery.speed( speed, easing, callback ),
6684 doAnimation = function() {
6685 // Operate on a copy of prop so per-property easing won't be lost
6686 var anim = Animation( this, jQuery.extend( {}, prop ), optall );
6688 // Empty animations, or finishing resolves immediately
6689 if ( empty || data_priv.get( this, "finish" ) ) {
6693 doAnimation.finish = doAnimation;
6695 return empty || optall.queue === false ?
6696 this.each( doAnimation ) :
6697 this.queue( optall.queue, doAnimation );
6699 stop: function( type, clearQueue, gotoEnd ) {
6700 var stopQueue = function( hooks ) {
6701 var stop = hooks.stop;
6706 if ( typeof type !== "string" ) {
6707 gotoEnd = clearQueue;
6711 if ( clearQueue && type !== false ) {
6712 this.queue( type || "fx", [] );
6715 return this.each(function() {
6717 index = type != null && type + "queueHooks",
6718 timers = jQuery.timers,
6719 data = data_priv.get( this );
6722 if ( data[ index ] && data[ index ].stop ) {
6723 stopQueue( data[ index ] );
6726 for ( index in data ) {
6727 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
6728 stopQueue( data[ index ] );
6733 for ( index = timers.length; index--; ) {
6734 if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
6735 timers[ index ].anim.stop( gotoEnd );
6737 timers.splice( index, 1 );
6741 // start the next in the queue if the last step wasn't forced
6742 // timers currently will call their complete callbacks, which will dequeue
6743 // but only if they were gotoEnd
6744 if ( dequeue || !gotoEnd ) {
6745 jQuery.dequeue( this, type );
6749 finish: function( type ) {
6750 if ( type !== false ) {
6751 type = type || "fx";
6753 return this.each(function() {
6755 data = data_priv.get( this ),
6756 queue = data[ type + "queue" ],
6757 hooks = data[ type + "queueHooks" ],
6758 timers = jQuery.timers,
6759 length = queue ? queue.length : 0;
6761 // enable finishing flag on private data
6764 // empty the queue first
6765 jQuery.queue( this, type, [] );
6767 if ( hooks && hooks.stop ) {
6768 hooks.stop.call( this, true );
6771 // look for any active animations, and finish them
6772 for ( index = timers.length; index--; ) {
6773 if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
6774 timers[ index ].anim.stop( true );
6775 timers.splice( index, 1 );
6779 // look for any animations in the old queue and finish them
6780 for ( index = 0; index < length; index++ ) {
6781 if ( queue[ index ] && queue[ index ].finish ) {
6782 queue[ index ].finish.call( this );
6786 // turn off finishing flag
6792 jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
6793 var cssFn = jQuery.fn[ name ];
6794 jQuery.fn[ name ] = function( speed, easing, callback ) {
6795 return speed == null || typeof speed === "boolean" ?
6796 cssFn.apply( this, arguments ) :
6797 this.animate( genFx( name, true ), speed, easing, callback );
6801 // Generate shortcuts for custom animations
6803 slideDown: genFx("show"),
6804 slideUp: genFx("hide"),
6805 slideToggle: genFx("toggle"),
6806 fadeIn: { opacity: "show" },
6807 fadeOut: { opacity: "hide" },
6808 fadeToggle: { opacity: "toggle" }
6809 }, function( name, props ) {
6810 jQuery.fn[ name ] = function( speed, easing, callback ) {
6811 return this.animate( props, speed, easing, callback );
6816 jQuery.fx.tick = function() {
6819 timers = jQuery.timers;
6821 fxNow = jQuery.now();
6823 for ( ; i < timers.length; i++ ) {
6824 timer = timers[ i ];
6825 // Checks the timer has not already been removed
6826 if ( !timer() && timers[ i ] === timer ) {
6827 timers.splice( i--, 1 );
6831 if ( !timers.length ) {
6837 jQuery.fx.timer = function( timer ) {
6838 jQuery.timers.push( timer );
6842 jQuery.timers.pop();
6846 jQuery.fx.interval = 13;
6848 jQuery.fx.start = function() {
6850 timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
6854 jQuery.fx.stop = function() {
6855 clearInterval( timerId );
6859 jQuery.fx.speeds = {
6867 // Based off of the plugin by Clint Helfers, with permission.
6868 // http://blindsignals.com/index.php/2009/07/jquery-delay/
6869 jQuery.fn.delay = function( time, type ) {
6870 time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
6871 type = type || "fx";
6873 return this.queue( type, function( next, hooks ) {
6874 var timeout = setTimeout( next, time );
6875 hooks.stop = function() {
6876 clearTimeout( timeout );
6883 var input = document.createElement( "input" ),
6884 select = document.createElement( "select" ),
6885 opt = select.appendChild( document.createElement( "option" ) );
6887 input.type = "checkbox";
6889 // Support: iOS 5.1, Android 4.x, Android 2.3
6890 // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
6891 support.checkOn = input.value !== "";
6893 // Must access the parent to make an option select properly
6894 // Support: IE9, IE10
6895 support.optSelected = opt.selected;
6897 // Make sure that the options inside disabled selects aren't marked as disabled
6898 // (WebKit marks them as disabled)
6899 select.disabled = true;
6900 support.optDisabled = !opt.disabled;
6902 // Check if an input maintains its value after becoming a radio
6903 // Support: IE9, IE10
6904 input = document.createElement( "input" );
6906 input.type = "radio";
6907 support.radioValue = input.value === "t";
6911 var nodeHook, boolHook,
6912 attrHandle = jQuery.expr.attrHandle;
6915 attr: function( name, value ) {
6916 return access( this, jQuery.attr, name, value, arguments.length > 1 );
6919 removeAttr: function( name ) {
6920 return this.each(function() {
6921 jQuery.removeAttr( this, name );
6927 attr: function( elem, name, value ) {
6929 nType = elem.nodeType;
6931 // don't get/set attributes on text, comment and attribute nodes
6932 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
6936 // Fallback to prop when attributes are not supported
6937 if ( typeof elem.getAttribute === strundefined ) {
6938 return jQuery.prop( elem, name, value );
6941 // All attributes are lowercase
6942 // Grab necessary hook if one is defined
6943 if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
6944 name = name.toLowerCase();
6945 hooks = jQuery.attrHooks[ name ] ||
6946 ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
6949 if ( value !== undefined ) {
6951 if ( value === null ) {
6952 jQuery.removeAttr( elem, name );
6954 } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
6958 elem.setAttribute( name, value + "" );
6962 } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
6966 ret = jQuery.find.attr( elem, name );
6968 // Non-existent attributes return null, we normalize to undefined
6969 return ret == null ?
6975 removeAttr: function( elem, value ) {
6978 attrNames = value && value.match( rnotwhite );
6980 if ( attrNames && elem.nodeType === 1 ) {
6981 while ( (name = attrNames[i++]) ) {
6982 propName = jQuery.propFix[ name ] || name;
6984 // Boolean attributes get special treatment (#10870)
6985 if ( jQuery.expr.match.bool.test( name ) ) {
6986 // Set corresponding property to false
6987 elem[ propName ] = false;
6990 elem.removeAttribute( name );
6997 set: function( elem, value ) {
6998 if ( !support.radioValue && value === "radio" &&
6999 jQuery.nodeName( elem, "input" ) ) {
7000 // Setting the type on a radio button after the value resets the value in IE6-9
7001 // Reset value to default in case type is set after value during creation
7002 var val = elem.value;
7003 elem.setAttribute( "type", value );
7014 // Hooks for boolean attributes
7016 set: function( elem, value, name ) {
7017 if ( value === false ) {
7018 // Remove boolean attributes when set to false
7019 jQuery.removeAttr( elem, name );
7021 elem.setAttribute( name, name );
7026 jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
7027 var getter = attrHandle[ name ] || jQuery.find.attr;
7029 attrHandle[ name ] = function( elem, name, isXML ) {
7032 // Avoid an infinite loop by temporarily removing this function from the getter
7033 handle = attrHandle[ name ];
7034 attrHandle[ name ] = ret;
7035 ret = getter( elem, name, isXML ) != null ?
7036 name.toLowerCase() :
7038 attrHandle[ name ] = handle;
7047 var rfocusable = /^(?:input|select|textarea|button)$/i;
7050 prop: function( name, value ) {
7051 return access( this, jQuery.prop, name, value, arguments.length > 1 );
7054 removeProp: function( name ) {
7055 return this.each(function() {
7056 delete this[ jQuery.propFix[ name ] || name ];
7064 "class": "className"
7067 prop: function( elem, name, value ) {
7068 var ret, hooks, notxml,
7069 nType = elem.nodeType;
7071 // don't get/set properties on text, comment and attribute nodes
7072 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
7076 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
7079 // Fix name and attach hooks
7080 name = jQuery.propFix[ name ] || name;
7081 hooks = jQuery.propHooks[ name ];
7084 if ( value !== undefined ) {
7085 return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
7087 ( elem[ name ] = value );
7090 return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
7098 get: function( elem ) {
7099 return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
7108 // Selectedness for an option in an optgroup can be inaccurate
7109 if ( !support.optSelected ) {
7110 jQuery.propHooks.selected = {
7111 get: function( elem ) {
7112 var parent = elem.parentNode;
7113 if ( parent && parent.parentNode ) {
7114 parent.parentNode.selectedIndex;
7133 jQuery.propFix[ this.toLowerCase() ] = this;
7139 var rclass = /[\t\r\n\f]/g;
7142 addClass: function( value ) {
7143 var classes, elem, cur, clazz, j, finalValue,
7144 proceed = typeof value === "string" && value,
7148 if ( jQuery.isFunction( value ) ) {
7149 return this.each(function( j ) {
7150 jQuery( this ).addClass( value.call( this, j, this.className ) );
7155 // The disjunction here is for better compressibility (see removeClass)
7156 classes = ( value || "" ).match( rnotwhite ) || [];
7158 for ( ; i < len; i++ ) {
7160 cur = elem.nodeType === 1 && ( elem.className ?
7161 ( " " + elem.className + " " ).replace( rclass, " " ) :
7167 while ( (clazz = classes[j++]) ) {
7168 if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
7173 // only assign if different to avoid unneeded rendering.
7174 finalValue = jQuery.trim( cur );
7175 if ( elem.className !== finalValue ) {
7176 elem.className = finalValue;
7185 removeClass: function( value ) {
7186 var classes, elem, cur, clazz, j, finalValue,
7187 proceed = arguments.length === 0 || typeof value === "string" && value,
7191 if ( jQuery.isFunction( value ) ) {
7192 return this.each(function( j ) {
7193 jQuery( this ).removeClass( value.call( this, j, this.className ) );
7197 classes = ( value || "" ).match( rnotwhite ) || [];
7199 for ( ; i < len; i++ ) {
7201 // This expression is here for better compressibility (see addClass)
7202 cur = elem.nodeType === 1 && ( elem.className ?
7203 ( " " + elem.className + " " ).replace( rclass, " " ) :
7209 while ( (clazz = classes[j++]) ) {
7210 // Remove *all* instances
7211 while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
7212 cur = cur.replace( " " + clazz + " ", " " );
7216 // only assign if different to avoid unneeded rendering.
7217 finalValue = value ? jQuery.trim( cur ) : "";
7218 if ( elem.className !== finalValue ) {
7219 elem.className = finalValue;
7228 toggleClass: function( value, stateVal ) {
7229 var type = typeof value;
7231 if ( typeof stateVal === "boolean" && type === "string" ) {
7232 return stateVal ? this.addClass( value ) : this.removeClass( value );
7235 if ( jQuery.isFunction( value ) ) {
7236 return this.each(function( i ) {
7237 jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
7241 return this.each(function() {
7242 if ( type === "string" ) {
7243 // toggle individual class names
7246 self = jQuery( this ),
7247 classNames = value.match( rnotwhite ) || [];
7249 while ( (className = classNames[ i++ ]) ) {
7250 // check each className given, space separated list
7251 if ( self.hasClass( className ) ) {
7252 self.removeClass( className );
7254 self.addClass( className );
7258 // Toggle whole class name
7259 } else if ( type === strundefined || type === "boolean" ) {
7260 if ( this.className ) {
7261 // store className if set
7262 data_priv.set( this, "__className__", this.className );
7265 // If the element has a class name or if we're passed "false",
7266 // then remove the whole classname (if there was one, the above saved it).
7267 // Otherwise bring back whatever was previously saved (if anything),
7268 // falling back to the empty string if nothing was stored.
7269 this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
7274 hasClass: function( selector ) {
7275 var className = " " + selector + " ",
7278 for ( ; i < l; i++ ) {
7279 if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
7291 var rreturn = /\r/g;
7294 val: function( value ) {
7295 var hooks, ret, isFunction,
7298 if ( !arguments.length ) {
7300 hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
7302 if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
7308 return typeof ret === "string" ?
7309 // handle most common string cases
7310 ret.replace(rreturn, "") :
7311 // handle cases where value is null/undef or number
7312 ret == null ? "" : ret;
7318 isFunction = jQuery.isFunction( value );
7320 return this.each(function( i ) {
7323 if ( this.nodeType !== 1 ) {
7328 val = value.call( this, i, jQuery( this ).val() );
7333 // Treat null/undefined as ""; convert numbers to string
7334 if ( val == null ) {
7337 } else if ( typeof val === "number" ) {
7340 } else if ( jQuery.isArray( val ) ) {
7341 val = jQuery.map( val, function( value ) {
7342 return value == null ? "" : value + "";
7346 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
7348 // If set returns undefined, fall back to normal setting
7349 if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
7359 get: function( elem ) {
7360 var val = jQuery.find.attr( elem, "value" );
7361 return val != null ?
7363 // Support: IE10-11+
7364 // option.text throws exceptions (#14686, #14858)
7365 jQuery.trim( jQuery.text( elem ) );
7369 get: function( elem ) {
7371 options = elem.options,
7372 index = elem.selectedIndex,
7373 one = elem.type === "select-one" || index < 0,
7374 values = one ? null : [],
7375 max = one ? index + 1 : options.length,
7380 // Loop through all the selected options
7381 for ( ; i < max; i++ ) {
7382 option = options[ i ];
7384 // IE6-9 doesn't update selected after form reset (#2551)
7385 if ( ( option.selected || i === index ) &&
7386 // Don't return options that are disabled or in a disabled optgroup
7387 ( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) &&
7388 ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
7390 // Get the specific value for the option
7391 value = jQuery( option ).val();
7393 // We don't need an array for one selects
7398 // Multi-Selects return an array
7399 values.push( value );
7406 set: function( elem, value ) {
7407 var optionSet, option,
7408 options = elem.options,
7409 values = jQuery.makeArray( value ),
7413 option = options[ i ];
7414 if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) {
7419 // force browsers to behave consistently when non-matching value is set
7421 elem.selectedIndex = -1;
7429 // Radios and checkboxes getter/setter
7430 jQuery.each([ "radio", "checkbox" ], function() {
7431 jQuery.valHooks[ this ] = {
7432 set: function( elem, value ) {
7433 if ( jQuery.isArray( value ) ) {
7434 return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
7438 if ( !support.checkOn ) {
7439 jQuery.valHooks[ this ].get = function( elem ) {
7441 // "" is returned instead of "on" if a value isn't specified
7442 return elem.getAttribute("value") === null ? "on" : elem.value;
7450 // Return jQuery for attributes-only inclusion
7453 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
7454 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
7455 "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
7457 // Handle event binding
7458 jQuery.fn[ name ] = function( data, fn ) {
7459 return arguments.length > 0 ?
7460 this.on( name, null, data, fn ) :
7461 this.trigger( name );
7466 hover: function( fnOver, fnOut ) {
7467 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
7470 bind: function( types, data, fn ) {
7471 return this.on( types, null, data, fn );
7473 unbind: function( types, fn ) {
7474 return this.off( types, null, fn );
7477 delegate: function( selector, types, data, fn ) {
7478 return this.on( types, selector, data, fn );
7480 undelegate: function( selector, types, fn ) {
7481 // ( namespace ) or ( selector, types [, fn] )
7482 return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
7487 var nonce = jQuery.now();
7489 var rquery = (/\?/);
7493 // Support: Android 2.3
7494 // Workaround failure to string-cast null input
7495 jQuery.parseJSON = function( data ) {
7496 return JSON.parse( data + "" );
7500 // Cross-browser xml parsing
7501 jQuery.parseXML = function( data ) {
7503 if ( !data || typeof data !== "string" ) {
7509 tmp = new DOMParser();
7510 xml = tmp.parseFromString( data, "text/xml" );
7515 if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
7516 jQuery.error( "Invalid XML: " + data );
7523 // Document location
7528 rts = /([?&])_=[^&]*/,
7529 rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
7530 // #7653, #8125, #8152: local protocol detection
7531 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
7532 rnoContent = /^(?:GET|HEAD)$/,
7533 rprotocol = /^\/\//,
7534 rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
7537 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
7538 * 2) These are called:
7539 * - BEFORE asking for a transport
7540 * - AFTER param serialization (s.data is a string if s.processData is true)
7541 * 3) key is the dataType
7542 * 4) the catchall symbol "*" can be used
7543 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
7547 /* Transports bindings
7548 * 1) key is the dataType
7549 * 2) the catchall symbol "*" can be used
7550 * 3) selection will start with transport dataType and THEN go to "*" if needed
7554 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
7555 allTypes = "*/".concat("*");
7557 // #8138, IE may throw an exception when accessing
7558 // a field from window.location if document.domain has been set
7560 ajaxLocation = location.href;
7562 // Use the href attribute of an A element
7563 // since IE will modify it given document.location
7564 ajaxLocation = document.createElement( "a" );
7565 ajaxLocation.href = "";
7566 ajaxLocation = ajaxLocation.href;
7569 // Segment location into parts
7570 ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
7572 // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
7573 function addToPrefiltersOrTransports( structure ) {
7575 // dataTypeExpression is optional and defaults to "*"
7576 return function( dataTypeExpression, func ) {
7578 if ( typeof dataTypeExpression !== "string" ) {
7579 func = dataTypeExpression;
7580 dataTypeExpression = "*";
7585 dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
7587 if ( jQuery.isFunction( func ) ) {
7588 // For each dataType in the dataTypeExpression
7589 while ( (dataType = dataTypes[i++]) ) {
7590 // Prepend if requested
7591 if ( dataType[0] === "+" ) {
7592 dataType = dataType.slice( 1 ) || "*";
7593 (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
7597 (structure[ dataType ] = structure[ dataType ] || []).push( func );
7604 // Base inspection function for prefilters and transports
7605 function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
7608 seekingTransport = ( structure === transports );
7610 function inspect( dataType ) {
7612 inspected[ dataType ] = true;
7613 jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
7614 var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
7615 if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
7616 options.dataTypes.unshift( dataTypeOrTransport );
7617 inspect( dataTypeOrTransport );
7619 } else if ( seekingTransport ) {
7620 return !( selected = dataTypeOrTransport );
7626 return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
7629 // A special extend for ajax options
7630 // that takes "flat" options (not to be deep extended)
7632 function ajaxExtend( target, src ) {
7634 flatOptions = jQuery.ajaxSettings.flatOptions || {};
7636 for ( key in src ) {
7637 if ( src[ key ] !== undefined ) {
7638 ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
7642 jQuery.extend( true, target, deep );
7648 /* Handles responses to an ajax request:
7649 * - finds the right dataType (mediates between content-type and expected dataType)
7650 * - returns the corresponding response
7652 function ajaxHandleResponses( s, jqXHR, responses ) {
7654 var ct, type, finalDataType, firstDataType,
7655 contents = s.contents,
7656 dataTypes = s.dataTypes;
7658 // Remove auto dataType and get content-type in the process
7659 while ( dataTypes[ 0 ] === "*" ) {
7661 if ( ct === undefined ) {
7662 ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
7666 // Check if we're dealing with a known content-type
7668 for ( type in contents ) {
7669 if ( contents[ type ] && contents[ type ].test( ct ) ) {
7670 dataTypes.unshift( type );
7676 // Check to see if we have a response for the expected dataType
7677 if ( dataTypes[ 0 ] in responses ) {
7678 finalDataType = dataTypes[ 0 ];
7680 // Try convertible dataTypes
7681 for ( type in responses ) {
7682 if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
7683 finalDataType = type;
7686 if ( !firstDataType ) {
7687 firstDataType = type;
7690 // Or just use first one
7691 finalDataType = finalDataType || firstDataType;
7694 // If we found a dataType
7695 // We add the dataType to the list if needed
7696 // and return the corresponding response
7697 if ( finalDataType ) {
7698 if ( finalDataType !== dataTypes[ 0 ] ) {
7699 dataTypes.unshift( finalDataType );
7701 return responses[ finalDataType ];
7705 /* Chain conversions given the request and the original response
7706 * Also sets the responseXXX fields on the jqXHR instance
7708 function ajaxConvert( s, response, jqXHR, isSuccess ) {
7709 var conv2, current, conv, tmp, prev,
7711 // Work with a copy of dataTypes in case we need to modify it for conversion
7712 dataTypes = s.dataTypes.slice();
7714 // Create converters map with lowercased keys
7715 if ( dataTypes[ 1 ] ) {
7716 for ( conv in s.converters ) {
7717 converters[ conv.toLowerCase() ] = s.converters[ conv ];
7721 current = dataTypes.shift();
7723 // Convert to each sequential dataType
7726 if ( s.responseFields[ current ] ) {
7727 jqXHR[ s.responseFields[ current ] ] = response;
7730 // Apply the dataFilter if provided
7731 if ( !prev && isSuccess && s.dataFilter ) {
7732 response = s.dataFilter( response, s.dataType );
7736 current = dataTypes.shift();
7740 // There's only work to do if current dataType is non-auto
7741 if ( current === "*" ) {
7745 // Convert response if prev dataType is non-auto and differs from current
7746 } else if ( prev !== "*" && prev !== current ) {
7748 // Seek a direct converter
7749 conv = converters[ prev + " " + current ] || converters[ "* " + current ];
7751 // If none found, seek a pair
7753 for ( conv2 in converters ) {
7755 // If conv2 outputs current
7756 tmp = conv2.split( " " );
7757 if ( tmp[ 1 ] === current ) {
7759 // If prev can be converted to accepted input
7760 conv = converters[ prev + " " + tmp[ 0 ] ] ||
7761 converters[ "* " + tmp[ 0 ] ];
7763 // Condense equivalence converters
7764 if ( conv === true ) {
7765 conv = converters[ conv2 ];
7767 // Otherwise, insert the intermediate dataType
7768 } else if ( converters[ conv2 ] !== true ) {
7770 dataTypes.unshift( tmp[ 1 ] );
7778 // Apply converter (if not an equivalence)
7779 if ( conv !== true ) {
7781 // Unless errors are allowed to bubble, catch and return them
7782 if ( conv && s[ "throws" ] ) {
7783 response = conv( response );
7786 response = conv( response );
7788 return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
7796 return { state: "success", data: response };
7801 // Counter for holding the number of active queries
7804 // Last-Modified header cache for next request
7811 isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
7815 contentType: "application/x-www-form-urlencoded; charset=UTF-8",
7832 xml: "application/xml, text/xml",
7833 json: "application/json, text/javascript"
7844 text: "responseText",
7845 json: "responseJSON"
7849 // Keys separate source (or catchall "*") and destination types with a single space
7852 // Convert anything to text
7855 // Text to html (true = no transformation)
7858 // Evaluate text as a json expression
7859 "text json": jQuery.parseJSON,
7861 // Parse text as xml
7862 "text xml": jQuery.parseXML
7865 // For options that shouldn't be deep extended:
7866 // you can add your own custom options here if
7867 // and when you create one that shouldn't be
7868 // deep extended (see ajaxExtend)
7875 // Creates a full fledged settings object into target
7876 // with both ajaxSettings and settings fields.
7877 // If target is omitted, writes into ajaxSettings.
7878 ajaxSetup: function( target, settings ) {
7881 // Building a settings object
7882 ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
7884 // Extending ajaxSettings
7885 ajaxExtend( jQuery.ajaxSettings, target );
7888 ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
7889 ajaxTransport: addToPrefiltersOrTransports( transports ),
7892 ajax: function( url, options ) {
7894 // If url is an object, simulate pre-1.5 signature
7895 if ( typeof url === "object" ) {
7900 // Force options to be an object
7901 options = options || {};
7904 // URL without anti-cache param
7907 responseHeadersString,
7911 // Cross-domain detection vars
7913 // To know if global events are to be dispatched
7917 // Create the final options object
7918 s = jQuery.ajaxSetup( {}, options ),
7919 // Callbacks context
7920 callbackContext = s.context || s,
7921 // Context for global events is callbackContext if it is a DOM node or jQuery collection
7922 globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
7923 jQuery( callbackContext ) :
7926 deferred = jQuery.Deferred(),
7927 completeDeferred = jQuery.Callbacks("once memory"),
7928 // Status-dependent callbacks
7929 statusCode = s.statusCode || {},
7930 // Headers (they are sent all at once)
7931 requestHeaders = {},
7932 requestHeadersNames = {},
7935 // Default abort message
7936 strAbort = "canceled",
7941 // Builds headers hashtable if needed
7942 getResponseHeader: function( key ) {
7944 if ( state === 2 ) {
7945 if ( !responseHeaders ) {
7946 responseHeaders = {};
7947 while ( (match = rheaders.exec( responseHeadersString )) ) {
7948 responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
7951 match = responseHeaders[ key.toLowerCase() ];
7953 return match == null ? null : match;
7957 getAllResponseHeaders: function() {
7958 return state === 2 ? responseHeadersString : null;
7961 // Caches the header
7962 setRequestHeader: function( name, value ) {
7963 var lname = name.toLowerCase();
7965 name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
7966 requestHeaders[ name ] = value;
7971 // Overrides response content-type header
7972 overrideMimeType: function( type ) {
7979 // Status-dependent callbacks
7980 statusCode: function( map ) {
7984 for ( code in map ) {
7985 // Lazy-add the new callback in a way that preserves old ones
7986 statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
7989 // Execute the appropriate callbacks
7990 jqXHR.always( map[ jqXHR.status ] );
7996 // Cancel the request
7997 abort: function( statusText ) {
7998 var finalText = statusText || strAbort;
8000 transport.abort( finalText );
8002 done( 0, finalText );
8008 deferred.promise( jqXHR ).complete = completeDeferred.add;
8009 jqXHR.success = jqXHR.done;
8010 jqXHR.error = jqXHR.fail;
8012 // Remove hash character (#7531: and string promotion)
8013 // Add protocol if not provided (prefilters might expect it)
8014 // Handle falsy url in the settings object (#10093: consistency with old signature)
8015 // We also use the url parameter if available
8016 s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
8017 .replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
8019 // Alias method option to type as per ticket #12004
8020 s.type = options.method || options.type || s.method || s.type;
8022 // Extract dataTypes list
8023 s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
8025 // A cross-domain request is in order when we have a protocol:host:port mismatch
8026 if ( s.crossDomain == null ) {
8027 parts = rurl.exec( s.url.toLowerCase() );
8028 s.crossDomain = !!( parts &&
8029 ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
8030 ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
8031 ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
8035 // Convert data if not already a string
8036 if ( s.data && s.processData && typeof s.data !== "string" ) {
8037 s.data = jQuery.param( s.data, s.traditional );
8041 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
8043 // If request was aborted inside a prefilter, stop there
8044 if ( state === 2 ) {
8048 // We can fire global events as of now if asked to
8049 fireGlobals = s.global;
8051 // Watch for a new set of requests
8052 if ( fireGlobals && jQuery.active++ === 0 ) {
8053 jQuery.event.trigger("ajaxStart");
8056 // Uppercase the type
8057 s.type = s.type.toUpperCase();
8059 // Determine if request has content
8060 s.hasContent = !rnoContent.test( s.type );
8062 // Save the URL in case we're toying with the If-Modified-Since
8063 // and/or If-None-Match header later on
8066 // More options handling for requests with no content
8067 if ( !s.hasContent ) {
8069 // If data is available, append data to url
8071 cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
8072 // #9682: remove data so that it's not used in an eventual retry
8076 // Add anti-cache in url if needed
8077 if ( s.cache === false ) {
8078 s.url = rts.test( cacheURL ) ?
8080 // If there is already a '_' parameter, set its value
8081 cacheURL.replace( rts, "$1_=" + nonce++ ) :
8083 // Otherwise add one to the end
8084 cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
8088 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8089 if ( s.ifModified ) {
8090 if ( jQuery.lastModified[ cacheURL ] ) {
8091 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
8093 if ( jQuery.etag[ cacheURL ] ) {
8094 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
8098 // Set the correct header, if data is being sent
8099 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
8100 jqXHR.setRequestHeader( "Content-Type", s.contentType );
8103 // Set the Accepts header for the server, depending on the dataType
8104 jqXHR.setRequestHeader(
8106 s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
8107 s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
8111 // Check for headers option
8112 for ( i in s.headers ) {
8113 jqXHR.setRequestHeader( i, s.headers[ i ] );
8116 // Allow custom headers/mimetypes and early abort
8117 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
8118 // Abort if not done already and return
8119 return jqXHR.abort();
8122 // aborting is no longer a cancellation
8125 // Install callbacks on deferreds
8126 for ( i in { success: 1, error: 1, complete: 1 } ) {
8127 jqXHR[ i ]( s[ i ] );
8131 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
8133 // If no transport, we auto-abort
8135 done( -1, "No Transport" );
8137 jqXHR.readyState = 1;
8139 // Send global event
8140 if ( fireGlobals ) {
8141 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
8144 if ( s.async && s.timeout > 0 ) {
8145 timeoutTimer = setTimeout(function() {
8146 jqXHR.abort("timeout");
8152 transport.send( requestHeaders, done );
8154 // Propagate exception as error if not done
8157 // Simply rethrow otherwise
8164 // Callback for when everything is done
8165 function done( status, nativeStatusText, responses, headers ) {
8166 var isSuccess, success, error, response, modified,
8167 statusText = nativeStatusText;
8170 if ( state === 2 ) {
8174 // State is "done" now
8177 // Clear timeout if it exists
8178 if ( timeoutTimer ) {
8179 clearTimeout( timeoutTimer );
8182 // Dereference transport for early garbage collection
8183 // (no matter how long the jqXHR object will be used)
8184 transport = undefined;
8186 // Cache response headers
8187 responseHeadersString = headers || "";
8190 jqXHR.readyState = status > 0 ? 4 : 0;
8192 // Determine if successful
8193 isSuccess = status >= 200 && status < 300 || status === 304;
8195 // Get response data
8197 response = ajaxHandleResponses( s, jqXHR, responses );
8200 // Convert no matter what (that way responseXXX fields are always set)
8201 response = ajaxConvert( s, response, jqXHR, isSuccess );
8203 // If successful, handle type chaining
8206 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8207 if ( s.ifModified ) {
8208 modified = jqXHR.getResponseHeader("Last-Modified");
8210 jQuery.lastModified[ cacheURL ] = modified;
8212 modified = jqXHR.getResponseHeader("etag");
8214 jQuery.etag[ cacheURL ] = modified;
8219 if ( status === 204 || s.type === "HEAD" ) {
8220 statusText = "nocontent";
8223 } else if ( status === 304 ) {
8224 statusText = "notmodified";
8226 // If we have data, let's convert it
8228 statusText = response.state;
8229 success = response.data;
8230 error = response.error;
8234 // We extract error from statusText
8235 // then normalize statusText and status for non-aborts
8237 if ( status || !statusText ) {
8238 statusText = "error";
8245 // Set data for the fake xhr object
8246 jqXHR.status = status;
8247 jqXHR.statusText = ( nativeStatusText || statusText ) + "";
8251 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
8253 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
8256 // Status-dependent callbacks
8257 jqXHR.statusCode( statusCode );
8258 statusCode = undefined;
8260 if ( fireGlobals ) {
8261 globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
8262 [ jqXHR, s, isSuccess ? success : error ] );
8266 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
8268 if ( fireGlobals ) {
8269 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
8270 // Handle the global AJAX counter
8271 if ( !( --jQuery.active ) ) {
8272 jQuery.event.trigger("ajaxStop");
8280 getJSON: function( url, data, callback ) {
8281 return jQuery.get( url, data, callback, "json" );
8284 getScript: function( url, callback ) {
8285 return jQuery.get( url, undefined, callback, "script" );
8289 jQuery.each( [ "get", "post" ], function( i, method ) {
8290 jQuery[ method ] = function( url, data, callback, type ) {
8291 // shift arguments if data argument was omitted
8292 if ( jQuery.isFunction( data ) ) {
8293 type = type || callback;
8298 return jQuery.ajax({
8308 // Attach a bunch of functions for handling common AJAX events
8309 jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
8310 jQuery.fn[ type ] = function( fn ) {
8311 return this.on( type, fn );
8316 jQuery._evalUrl = function( url ) {
8317 return jQuery.ajax({
8329 wrapAll: function( html ) {
8332 if ( jQuery.isFunction( html ) ) {
8333 return this.each(function( i ) {
8334 jQuery( this ).wrapAll( html.call(this, i) );
8340 // The elements to wrap the target around
8341 wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
8343 if ( this[ 0 ].parentNode ) {
8344 wrap.insertBefore( this[ 0 ] );
8347 wrap.map(function() {
8350 while ( elem.firstElementChild ) {
8351 elem = elem.firstElementChild;
8361 wrapInner: function( html ) {
8362 if ( jQuery.isFunction( html ) ) {
8363 return this.each(function( i ) {
8364 jQuery( this ).wrapInner( html.call(this, i) );
8368 return this.each(function() {
8369 var self = jQuery( this ),
8370 contents = self.contents();
8372 if ( contents.length ) {
8373 contents.wrapAll( html );
8376 self.append( html );
8381 wrap: function( html ) {
8382 var isFunction = jQuery.isFunction( html );
8384 return this.each(function( i ) {
8385 jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
8389 unwrap: function() {
8390 return this.parent().each(function() {
8391 if ( !jQuery.nodeName( this, "body" ) ) {
8392 jQuery( this ).replaceWith( this.childNodes );
8399 jQuery.expr.filters.hidden = function( elem ) {
8400 // Support: Opera <= 12.12
8401 // Opera reports offsetWidths and offsetHeights less than zero on some elements
8402 return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
8404 jQuery.expr.filters.visible = function( elem ) {
8405 return !jQuery.expr.filters.hidden( elem );
8414 rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
8415 rsubmittable = /^(?:input|select|textarea|keygen)/i;
8417 function buildParams( prefix, obj, traditional, add ) {
8420 if ( jQuery.isArray( obj ) ) {
8421 // Serialize array item.
8422 jQuery.each( obj, function( i, v ) {
8423 if ( traditional || rbracket.test( prefix ) ) {
8424 // Treat each array item as a scalar.
8428 // Item is non-scalar (array or object), encode its numeric index.
8429 buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
8433 } else if ( !traditional && jQuery.type( obj ) === "object" ) {
8434 // Serialize object item.
8435 for ( name in obj ) {
8436 buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
8440 // Serialize scalar item.
8445 // Serialize an array of form elements or a set of
8446 // key/values into a query string
8447 jQuery.param = function( a, traditional ) {
8450 add = function( key, value ) {
8451 // If value is a function, invoke it and return its value
8452 value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
8453 s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
8456 // Set traditional to true for jQuery <= 1.3.2 behavior.
8457 if ( traditional === undefined ) {
8458 traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
8461 // If an array was passed in, assume that it is an array of form elements.
8462 if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
8463 // Serialize the form elements
8464 jQuery.each( a, function() {
8465 add( this.name, this.value );
8469 // If traditional, encode the "old" way (the way 1.3.2 or older
8470 // did it), otherwise encode params recursively.
8471 for ( prefix in a ) {
8472 buildParams( prefix, a[ prefix ], traditional, add );
8476 // Return the resulting serialization
8477 return s.join( "&" ).replace( r20, "+" );
8481 serialize: function() {
8482 return jQuery.param( this.serializeArray() );
8484 serializeArray: function() {
8485 return this.map(function() {
8486 // Can add propHook for "elements" to filter or add form elements
8487 var elements = jQuery.prop( this, "elements" );
8488 return elements ? jQuery.makeArray( elements ) : this;
8490 .filter(function() {
8491 var type = this.type;
8493 // Use .is( ":disabled" ) so that fieldset[disabled] works
8494 return this.name && !jQuery( this ).is( ":disabled" ) &&
8495 rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
8496 ( this.checked || !rcheckableType.test( type ) );
8498 .map(function( i, elem ) {
8499 var val = jQuery( this ).val();
8501 return val == null ?
8503 jQuery.isArray( val ) ?
8504 jQuery.map( val, function( val ) {
8505 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
8507 { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
8513 jQuery.ajaxSettings.xhr = function() {
8515 return new XMLHttpRequest();
8521 xhrSuccessStatus = {
8522 // file protocol always yields status code 0, assume 200
8525 // #1450: sometimes IE returns 1223 when it should be 204
8528 xhrSupported = jQuery.ajaxSettings.xhr();
8531 // Open requests must be manually aborted on unload (#5280)
8532 if ( window.ActiveXObject ) {
8533 jQuery( window ).on( "unload", function() {
8534 for ( var key in xhrCallbacks ) {
8535 xhrCallbacks[ key ]();
8540 support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
8541 support.ajax = xhrSupported = !!xhrSupported;
8543 jQuery.ajaxTransport(function( options ) {
8546 // Cross domain only allowed if supported through XMLHttpRequest
8547 if ( support.cors || xhrSupported && !options.crossDomain ) {
8549 send: function( headers, complete ) {
8551 xhr = options.xhr(),
8554 xhr.open( options.type, options.url, options.async, options.username, options.password );
8556 // Apply custom fields if provided
8557 if ( options.xhrFields ) {
8558 for ( i in options.xhrFields ) {
8559 xhr[ i ] = options.xhrFields[ i ];
8563 // Override mime type if needed
8564 if ( options.mimeType && xhr.overrideMimeType ) {
8565 xhr.overrideMimeType( options.mimeType );
8568 // X-Requested-With header
8569 // For cross-domain requests, seeing as conditions for a preflight are
8570 // akin to a jigsaw puzzle, we simply never set it to be sure.
8571 // (it can always be set on a per-request basis or even using ajaxSetup)
8572 // For same-domain requests, won't change header if already provided.
8573 if ( !options.crossDomain && !headers["X-Requested-With"] ) {
8574 headers["X-Requested-With"] = "XMLHttpRequest";
8578 for ( i in headers ) {
8579 xhr.setRequestHeader( i, headers[ i ] );
8583 callback = function( type ) {
8586 delete xhrCallbacks[ id ];
8587 callback = xhr.onload = xhr.onerror = null;
8589 if ( type === "abort" ) {
8591 } else if ( type === "error" ) {
8593 // file: protocol always yields status 0; see #8605, #14207
8599 xhrSuccessStatus[ xhr.status ] || xhr.status,
8602 // Accessing binary-data responseText throws an exception
8604 typeof xhr.responseText === "string" ? {
8605 text: xhr.responseText
8607 xhr.getAllResponseHeaders()
8615 xhr.onload = callback();
8616 xhr.onerror = callback("error");
8618 // Create the abort callback
8619 callback = xhrCallbacks[ id ] = callback("abort");
8622 // Do send the request (this may raise an exception)
8623 xhr.send( options.hasContent && options.data || null );
8625 // #14683: Only rethrow if this hasn't been notified as an error yet
8644 // Install script dataType
8647 script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
8650 script: /(?:java|ecma)script/
8653 "text script": function( text ) {
8654 jQuery.globalEval( text );
8660 // Handle cache's special case and crossDomain
8661 jQuery.ajaxPrefilter( "script", function( s ) {
8662 if ( s.cache === undefined ) {
8665 if ( s.crossDomain ) {
8670 // Bind script tag hack transport
8671 jQuery.ajaxTransport( "script", function( s ) {
8672 // This transport only deals with cross domain requests
8673 if ( s.crossDomain ) {
8674 var script, callback;
8676 send: function( _, complete ) {
8677 script = jQuery("<script>").prop({
8679 charset: s.scriptCharset,
8683 callback = function( evt ) {
8687 complete( evt.type === "error" ? 404 : 200, evt.type );
8691 document.head.appendChild( script[ 0 ] );
8705 var oldCallbacks = [],
8706 rjsonp = /(=)\?(?=&|$)|\?\?/;
8708 // Default jsonp settings
8711 jsonpCallback: function() {
8712 var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
8713 this[ callback ] = true;
8718 // Detect, normalize options and install callbacks for jsonp requests
8719 jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
8721 var callbackName, overwritten, responseContainer,
8722 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
8724 typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
8727 // Handle iff the expected data type is "jsonp" or we have a parameter to set
8728 if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
8730 // Get callback name, remembering preexisting value associated with it
8731 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
8735 // Insert callback into url or form data
8737 s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
8738 } else if ( s.jsonp !== false ) {
8739 s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
8742 // Use data converter to retrieve json after script execution
8743 s.converters["script json"] = function() {
8744 if ( !responseContainer ) {
8745 jQuery.error( callbackName + " was not called" );
8747 return responseContainer[ 0 ];
8750 // force json dataType
8751 s.dataTypes[ 0 ] = "json";
8754 overwritten = window[ callbackName ];
8755 window[ callbackName ] = function() {
8756 responseContainer = arguments;
8759 // Clean-up function (fires after converters)
8760 jqXHR.always(function() {
8761 // Restore preexisting value
8762 window[ callbackName ] = overwritten;
8764 // Save back as free
8765 if ( s[ callbackName ] ) {
8766 // make sure that re-using the options doesn't screw things around
8767 s.jsonpCallback = originalSettings.jsonpCallback;
8769 // save the callback name for future use
8770 oldCallbacks.push( callbackName );
8773 // Call if it was a function and we have a response
8774 if ( responseContainer && jQuery.isFunction( overwritten ) ) {
8775 overwritten( responseContainer[ 0 ] );
8778 responseContainer = overwritten = undefined;
8781 // Delegate to script
8789 // data: string of html
8790 // context (optional): If specified, the fragment will be created in this context, defaults to document
8791 // keepScripts (optional): If true, will include scripts passed in the html string
8792 jQuery.parseHTML = function( data, context, keepScripts ) {
8793 if ( !data || typeof data !== "string" ) {
8796 if ( typeof context === "boolean" ) {
8797 keepScripts = context;
8800 context = context || document;
8802 var parsed = rsingleTag.exec( data ),
8803 scripts = !keepScripts && [];
8807 return [ context.createElement( parsed[1] ) ];
8810 parsed = jQuery.buildFragment( [ data ], context, scripts );
8812 if ( scripts && scripts.length ) {
8813 jQuery( scripts ).remove();
8816 return jQuery.merge( [], parsed.childNodes );
8820 // Keep a copy of the old load method
8821 var _load = jQuery.fn.load;
8824 * Load a url into a page
8826 jQuery.fn.load = function( url, params, callback ) {
8827 if ( typeof url !== "string" && _load ) {
8828 return _load.apply( this, arguments );
8831 var selector, type, response,
8833 off = url.indexOf(" ");
8836 selector = jQuery.trim( url.slice( off ) );
8837 url = url.slice( 0, off );
8840 // If it's a function
8841 if ( jQuery.isFunction( params ) ) {
8843 // We assume that it's the callback
8847 // Otherwise, build a param string
8848 } else if ( params && typeof params === "object" ) {
8852 // If we have elements to modify, make the request
8853 if ( self.length > 0 ) {
8857 // if "type" variable is undefined, then "GET" method will be used
8861 }).done(function( responseText ) {
8863 // Save response for use in complete callback
8864 response = arguments;
8866 self.html( selector ?
8868 // If a selector was specified, locate the right elements in a dummy div
8869 // Exclude scripts to avoid IE 'Permission Denied' errors
8870 jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
8872 // Otherwise use the full result
8875 }).complete( callback && function( jqXHR, status ) {
8876 self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
8886 jQuery.expr.filters.animated = function( elem ) {
8887 return jQuery.grep(jQuery.timers, function( fn ) {
8888 return elem === fn.elem;
8895 var docElem = window.document.documentElement;
8898 * Gets a window from an element
8900 function getWindow( elem ) {
8901 return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
8905 setOffset: function( elem, options, i ) {
8906 var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
8907 position = jQuery.css( elem, "position" ),
8908 curElem = jQuery( elem ),
8911 // Set position first, in-case top/left are set even on static elem
8912 if ( position === "static" ) {
8913 elem.style.position = "relative";
8916 curOffset = curElem.offset();
8917 curCSSTop = jQuery.css( elem, "top" );
8918 curCSSLeft = jQuery.css( elem, "left" );
8919 calculatePosition = ( position === "absolute" || position === "fixed" ) &&
8920 ( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
8922 // Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
8923 if ( calculatePosition ) {
8924 curPosition = curElem.position();
8925 curTop = curPosition.top;
8926 curLeft = curPosition.left;
8929 curTop = parseFloat( curCSSTop ) || 0;
8930 curLeft = parseFloat( curCSSLeft ) || 0;
8933 if ( jQuery.isFunction( options ) ) {
8934 options = options.call( elem, i, curOffset );
8937 if ( options.top != null ) {
8938 props.top = ( options.top - curOffset.top ) + curTop;
8940 if ( options.left != null ) {
8941 props.left = ( options.left - curOffset.left ) + curLeft;
8944 if ( "using" in options ) {
8945 options.using.call( elem, props );
8948 curElem.css( props );
8954 offset: function( options ) {
8955 if ( arguments.length ) {
8956 return options === undefined ?
8958 this.each(function( i ) {
8959 jQuery.offset.setOffset( this, options, i );
8965 box = { top: 0, left: 0 },
8966 doc = elem && elem.ownerDocument;
8972 docElem = doc.documentElement;
8974 // Make sure it's not a disconnected DOM node
8975 if ( !jQuery.contains( docElem, elem ) ) {
8979 // If we don't have gBCR, just use 0,0 rather than error
8980 // BlackBerry 5, iOS 3 (original iPhone)
8981 if ( typeof elem.getBoundingClientRect !== strundefined ) {
8982 box = elem.getBoundingClientRect();
8984 win = getWindow( doc );
8986 top: box.top + win.pageYOffset - docElem.clientTop,
8987 left: box.left + win.pageXOffset - docElem.clientLeft
8991 position: function() {
8996 var offsetParent, offset,
8998 parentOffset = { top: 0, left: 0 };
9000 // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
9001 if ( jQuery.css( elem, "position" ) === "fixed" ) {
9002 // We assume that getBoundingClientRect is available when computed position is fixed
9003 offset = elem.getBoundingClientRect();
9006 // Get *real* offsetParent
9007 offsetParent = this.offsetParent();
9009 // Get correct offsets
9010 offset = this.offset();
9011 if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
9012 parentOffset = offsetParent.offset();
9015 // Add offsetParent borders
9016 parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
9017 parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
9020 // Subtract parent offsets and element margins
9022 top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
9023 left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
9027 offsetParent: function() {
9028 return this.map(function() {
9029 var offsetParent = this.offsetParent || docElem;
9031 while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
9032 offsetParent = offsetParent.offsetParent;
9035 return offsetParent || docElem;
9040 // Create scrollLeft and scrollTop methods
9041 jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
9042 var top = "pageYOffset" === prop;
9044 jQuery.fn[ method ] = function( val ) {
9045 return access( this, function( elem, method, val ) {
9046 var win = getWindow( elem );
9048 if ( val === undefined ) {
9049 return win ? win[ prop ] : elem[ method ];
9054 !top ? val : window.pageXOffset,
9055 top ? val : window.pageYOffset
9059 elem[ method ] = val;
9061 }, method, val, arguments.length, null );
9065 // Add the top/left cssHooks using jQuery.fn.position
9066 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
9067 // getComputedStyle returns percent when specified for top/left/bottom/right
9068 // rather than make the css module depend on the offset module, we just check for it here
9069 jQuery.each( [ "top", "left" ], function( i, prop ) {
9070 jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
9071 function( elem, computed ) {
9073 computed = curCSS( elem, prop );
9074 // if curCSS returns percentage, fallback to offset
9075 return rnumnonpx.test( computed ) ?
9076 jQuery( elem ).position()[ prop ] + "px" :
9084 // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
9085 jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
9086 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
9087 // margin is only for outerHeight, outerWidth
9088 jQuery.fn[ funcName ] = function( margin, value ) {
9089 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
9090 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
9092 return access( this, function( elem, type, value ) {
9095 if ( jQuery.isWindow( elem ) ) {
9096 // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
9097 // isn't a whole lot we can do. See pull request at this URL for discussion:
9098 // https://github.com/jquery/jquery/pull/764
9099 return elem.document.documentElement[ "client" + name ];
9102 // Get document width or height
9103 if ( elem.nodeType === 9 ) {
9104 doc = elem.documentElement;
9106 // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
9107 // whichever is greatest
9109 elem.body[ "scroll" + name ], doc[ "scroll" + name ],
9110 elem.body[ "offset" + name ], doc[ "offset" + name ],
9111 doc[ "client" + name ]
9115 return value === undefined ?
9116 // Get width or height on the element, requesting but not forcing parseFloat
9117 jQuery.css( elem, type, extra ) :
9119 // Set width or height on the element
9120 jQuery.style( elem, type, value, extra );
9121 }, type, chainable ? margin : undefined, chainable, null );
9127 // The number of elements contained in the matched element set
9128 jQuery.fn.size = function() {
9132 jQuery.fn.andSelf = jQuery.fn.addBack;
9137 // Register as a named AMD module, since jQuery can be concatenated with other
9138 // files that may use define, but not via a proper concatenation script that
9139 // understands anonymous AMD modules. A named AMD is safest and most robust
9140 // way to register. Lowercase jquery is used because AMD module names are
9141 // derived from file names, and jQuery is normally delivered in a lowercase
9142 // file name. Do this after creating the global so that if an AMD module wants
9143 // to call noConflict to hide this version of jQuery, it will work.
9145 // Note that for maximum portability, libraries that are not jQuery should
9146 // declare themselves as anonymous modules, and avoid setting a global if an
9147 // AMD loader is present. jQuery is a special case. For more information, see
9148 // https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
9150 if ( typeof define === "function" && define.amd ) {
9151 define( "jquery", [], function() {
9160 // Map over jQuery in case of overwrite
9161 _jQuery = window.jQuery,
9163 // Map over the $ in case of overwrite
9166 jQuery.noConflict = function( deep ) {
9167 if ( window.$ === jQuery ) {
9171 if ( deep && window.jQuery === jQuery ) {
9172 window.jQuery = _jQuery;
9178 // Expose jQuery and $ identifiers, even in
9179 // AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
9180 // and CommonJS for browser emulators (#13566)
9181 if ( typeof noGlobal === strundefined ) {
9182 window.jQuery = window.$ = jQuery;
9193 * @license AngularJS v1.5.5
9194 * (c) 2010-2016 Google, Inc. http://angularjs.org
9198 var _jQuery = window.jQuery.noConflict(true);
9203 * This object provides a utility for producing rich Error messages within
9204 * Angular. It can be called as follows:
9206 * var exampleMinErr = minErr('example');
9207 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
9209 * The above creates an instance of minErr in the example namespace. The
9210 * resulting error will have a namespaced error code of example.one. The
9211 * resulting error will replace {0} with the value of foo, and {1} with the
9212 * value of bar. The object is not restricted in the number of arguments it can
9215 * If fewer arguments are specified than necessary for interpolation, the extra
9216 * interpolation markers will be preserved in the final string.
9218 * Since data will be parsed statically during a build step, some restrictions
9219 * are applied with respect to how minErr instances are created and called.
9220 * Instances should have names of the form namespaceMinErr for a minErr created
9221 * using minErr('namespace') . Error codes, namespaces and template strings
9222 * should all be static strings, not variables or general expressions.
9224 * @param {string} module The namespace to use for the new minErr instance.
9225 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
9226 * error from returned function, for cases when a particular type of error is useful.
9227 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
9230 function minErr(module, ErrorConstructor) {
9231 ErrorConstructor = ErrorConstructor || Error;
9233 var SKIP_INDEXES = 2;
9235 var templateArgs = arguments,
9236 code = templateArgs[0],
9237 message = '[' + (module ? module + ':' : '') + code + '] ',
9238 template = templateArgs[1],
9241 message += template.replace(/\{\d+\}/g, function(match) {
9242 var index = +match.slice(1, -1),
9243 shiftedIndex = index + SKIP_INDEXES;
9245 if (shiftedIndex < templateArgs.length) {
9246 return toDebugString(templateArgs[shiftedIndex]);
9252 message += '\nhttp://errors.angularjs.org/1.5.5/' +
9253 (module ? module + '/' : '') + code;
9255 for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
9256 message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
9257 encodeURIComponent(toDebugString(templateArgs[i]));
9260 return new ErrorConstructor(message);
9264 /* We need to tell jshint what variables are being exported */
9265 /* global angular: true,
9274 angularModule: true,
9276 REGEX_STRING_REGEXP: true,
9277 VALIDITY_STATE_PROPERTY: true,
9281 manualLowercase: true,
9282 manualUppercase: true,
9286 forEachSorted: true,
9287 reverseParams: true,
9300 isBlankObject: true,
9313 isPromiseLike: true,
9315 escapeForRegexp: true,
9328 toJsonReplacer: true,
9331 convertTimezoneToLocal: true,
9332 timezoneToOffset: true,
9334 tryDecodeURIComponent: true,
9335 parseKeyValue: true,
9337 encodeUriSegment: true,
9338 encodeUriQuery: true,
9341 getTestability: true,
9346 assertNotHasOwnProperty: true,
9348 getBlockNodes: true,
9349 hasOwnProperty: true,
9352 NODE_TYPE_ELEMENT: true,
9353 NODE_TYPE_ATTRIBUTE: true,
9354 NODE_TYPE_TEXT: true,
9355 NODE_TYPE_COMMENT: true,
9356 NODE_TYPE_DOCUMENT: true,
9357 NODE_TYPE_DOCUMENT_FRAGMENT: true,
9360 ////////////////////////////////////
9369 * # ng (core module)
9370 * The ng module is loaded by default when an AngularJS application is started. The module itself
9371 * contains the essential components for an AngularJS application to function. The table below
9372 * lists a high level breakdown of each of the services/factories, filters, directives and testing
9373 * components available within this core module.
9375 * <div doc-module-components="ng"></div>
9378 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
9380 // The name of a form control's ValidityState property.
9381 // This is used so that it's possible for internal tests to create mock ValidityStates.
9382 var VALIDITY_STATE_PROPERTY = 'validity';
9384 var hasOwnProperty = Object.prototype.hasOwnProperty;
9386 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
9387 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
9390 var manualLowercase = function(s) {
9391 /* jshint bitwise: false */
9393 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
9396 var manualUppercase = function(s) {
9397 /* jshint bitwise: false */
9399 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
9404 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
9405 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
9406 // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
9407 if ('i' !== 'I'.toLowerCase()) {
9408 lowercase = manualLowercase;
9409 uppercase = manualUppercase;
9414 msie, // holds major version number for IE, or NaN if UA is not IE.
9415 jqLite, // delay binding since jQuery could be loaded after us.
9416 jQuery, // delay binding
9420 toString = Object.prototype.toString,
9421 getPrototypeOf = Object.getPrototypeOf,
9422 ngMinErr = minErr('ng'),
9424 /** @name angular */
9425 angular = window.angular || (window.angular = {}),
9430 * documentMode is an IE-only property
9431 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
9433 msie = window.document.documentMode;
9439 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
9442 function isArrayLike(obj) {
9444 // `null`, `undefined` and `window` are not array-like
9445 if (obj == null || isWindow(obj)) return false;
9447 // arrays, strings and jQuery/jqLite objects are array like
9448 // * jqLite is either the jQuery or jqLite constructor function
9449 // * we have to check the existence of jqLite first as this method is called
9450 // via the forEach method when constructing the jqLite object in the first place
9451 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
9453 // Support: iOS 8.2 (not reproducible in simulator)
9454 // "length" in obj used to prevent JIT error (gh-11508)
9455 var length = "length" in Object(obj) && obj.length;
9457 // NodeList objects (with `item` method) and
9458 // other objects with suitable length characteristics are array-like
9459 return isNumber(length) &&
9460 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
9466 * @name angular.forEach
9471 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
9472 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
9473 * is the value of an object property or an array element, `key` is the object property key or
9474 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
9476 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
9477 * using the `hasOwnProperty` method.
9480 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
9481 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
9482 * return the value provided.
9485 var values = {name: 'misko', gender: 'male'};
9487 angular.forEach(values, function(value, key) {
9488 this.push(key + ': ' + value);
9490 expect(log).toEqual(['name: misko', 'gender: male']);
9493 * @param {Object|Array} obj Object to iterate over.
9494 * @param {Function} iterator Iterator function.
9495 * @param {Object=} context Object to become context (`this`) for the iterator function.
9496 * @returns {Object|Array} Reference to `obj`.
9499 function forEach(obj, iterator, context) {
9502 if (isFunction(obj)) {
9504 // Need to check if hasOwnProperty exists,
9505 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
9506 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
9507 iterator.call(context, obj[key], key, obj);
9510 } else if (isArray(obj) || isArrayLike(obj)) {
9511 var isPrimitive = typeof obj !== 'object';
9512 for (key = 0, length = obj.length; key < length; key++) {
9513 if (isPrimitive || key in obj) {
9514 iterator.call(context, obj[key], key, obj);
9517 } else if (obj.forEach && obj.forEach !== forEach) {
9518 obj.forEach(iterator, context, obj);
9519 } else if (isBlankObject(obj)) {
9520 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
9522 iterator.call(context, obj[key], key, obj);
9524 } else if (typeof obj.hasOwnProperty === 'function') {
9525 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
9527 if (obj.hasOwnProperty(key)) {
9528 iterator.call(context, obj[key], key, obj);
9532 // Slow path for objects which do not have a method `hasOwnProperty`
9534 if (hasOwnProperty.call(obj, key)) {
9535 iterator.call(context, obj[key], key, obj);
9543 function forEachSorted(obj, iterator, context) {
9544 var keys = Object.keys(obj).sort();
9545 for (var i = 0; i < keys.length; i++) {
9546 iterator.call(context, obj[keys[i]], keys[i]);
9553 * when using forEach the params are value, key, but it is often useful to have key, value.
9554 * @param {function(string, *)} iteratorFn
9555 * @returns {function(*, string)}
9557 function reverseParams(iteratorFn) {
9558 return function(value, key) {iteratorFn(key, value);};
9562 * A consistent way of creating unique IDs in angular.
9564 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
9565 * we hit number precision issues in JavaScript.
9567 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
9569 * @returns {number} an unique alpha-numeric string
9571 function nextUid() {
9577 * Set or clear the hashkey for an object.
9579 * @param h the hashkey (!truthy to delete the hashkey)
9581 function setHashKey(obj, h) {
9585 delete obj.$$hashKey;
9590 function baseExtend(dst, objs, deep) {
9591 var h = dst.$$hashKey;
9593 for (var i = 0, ii = objs.length; i < ii; ++i) {
9595 if (!isObject(obj) && !isFunction(obj)) continue;
9596 var keys = Object.keys(obj);
9597 for (var j = 0, jj = keys.length; j < jj; j++) {
9601 if (deep && isObject(src)) {
9603 dst[key] = new Date(src.valueOf());
9604 } else if (isRegExp(src)) {
9605 dst[key] = new RegExp(src);
9606 } else if (src.nodeName) {
9607 dst[key] = src.cloneNode(true);
9608 } else if (isElement(src)) {
9609 dst[key] = src.clone();
9611 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
9612 baseExtend(dst[key], [src], true);
9626 * @name angular.extend
9631 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
9632 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
9633 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
9635 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
9636 * {@link angular.merge} for this.
9638 * @param {Object} dst Destination object.
9639 * @param {...Object} src Source object(s).
9640 * @returns {Object} Reference to `dst`.
9642 function extend(dst) {
9643 return baseExtend(dst, slice.call(arguments, 1), false);
9649 * @name angular.merge
9654 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
9655 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
9656 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
9658 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
9659 * objects, performing a deep copy.
9661 * @param {Object} dst Destination object.
9662 * @param {...Object} src Source object(s).
9663 * @returns {Object} Reference to `dst`.
9665 function merge(dst) {
9666 return baseExtend(dst, slice.call(arguments, 1), true);
9671 function toInt(str) {
9672 return parseInt(str, 10);
9676 function inherit(parent, extra) {
9677 return extend(Object.create(parent), extra);
9682 * @name angular.noop
9687 * A function that performs no operations. This function can be useful when writing code in the
9690 function foo(callback) {
9691 var result = calculateResult();
9692 (callback || angular.noop)(result);
9702 * @name angular.identity
9707 * A function that returns its first argument. This function is useful when writing code in the
9711 function transformer(transformationFn, value) {
9712 return (transformationFn || angular.identity)(value);
9715 * @param {*} value to be returned.
9716 * @returns {*} the value passed in.
9718 function identity($) {return $;}
9719 identity.$inject = [];
9722 function valueFn(value) {return function valueRef() {return value;};}
9724 function hasCustomToString(obj) {
9725 return isFunction(obj.toString) && obj.toString !== toString;
9731 * @name angular.isUndefined
9736 * Determines if a reference is undefined.
9738 * @param {*} value Reference to check.
9739 * @returns {boolean} True if `value` is undefined.
9741 function isUndefined(value) {return typeof value === 'undefined';}
9746 * @name angular.isDefined
9751 * Determines if a reference is defined.
9753 * @param {*} value Reference to check.
9754 * @returns {boolean} True if `value` is defined.
9756 function isDefined(value) {return typeof value !== 'undefined';}
9761 * @name angular.isObject
9766 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
9767 * considered to be objects. Note that JavaScript arrays are objects.
9769 * @param {*} value Reference to check.
9770 * @returns {boolean} True if `value` is an `Object` but not `null`.
9772 function isObject(value) {
9773 // http://jsperf.com/isobject4
9774 return value !== null && typeof value === 'object';
9779 * Determine if a value is an object with a null prototype
9781 * @returns {boolean} True if `value` is an `Object` with a null prototype
9783 function isBlankObject(value) {
9784 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
9790 * @name angular.isString
9795 * Determines if a reference is a `String`.
9797 * @param {*} value Reference to check.
9798 * @returns {boolean} True if `value` is a `String`.
9800 function isString(value) {return typeof value === 'string';}
9805 * @name angular.isNumber
9810 * Determines if a reference is a `Number`.
9812 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
9814 * If you wish to exclude these then you can use the native
9815 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
9818 * @param {*} value Reference to check.
9819 * @returns {boolean} True if `value` is a `Number`.
9821 function isNumber(value) {return typeof value === 'number';}
9826 * @name angular.isDate
9831 * Determines if a value is a date.
9833 * @param {*} value Reference to check.
9834 * @returns {boolean} True if `value` is a `Date`.
9836 function isDate(value) {
9837 return toString.call(value) === '[object Date]';
9843 * @name angular.isArray
9848 * Determines if a reference is an `Array`.
9850 * @param {*} value Reference to check.
9851 * @returns {boolean} True if `value` is an `Array`.
9853 var isArray = Array.isArray;
9857 * @name angular.isFunction
9862 * Determines if a reference is a `Function`.
9864 * @param {*} value Reference to check.
9865 * @returns {boolean} True if `value` is a `Function`.
9867 function isFunction(value) {return typeof value === 'function';}
9871 * Determines if a value is a regular expression object.
9874 * @param {*} value Reference to check.
9875 * @returns {boolean} True if `value` is a `RegExp`.
9877 function isRegExp(value) {
9878 return toString.call(value) === '[object RegExp]';
9883 * Checks if `obj` is a window object.
9886 * @param {*} obj Object to check
9887 * @returns {boolean} True if `obj` is a window obj.
9889 function isWindow(obj) {
9890 return obj && obj.window === obj;
9894 function isScope(obj) {
9895 return obj && obj.$evalAsync && obj.$watch;
9899 function isFile(obj) {
9900 return toString.call(obj) === '[object File]';
9904 function isFormData(obj) {
9905 return toString.call(obj) === '[object FormData]';
9909 function isBlob(obj) {
9910 return toString.call(obj) === '[object Blob]';
9914 function isBoolean(value) {
9915 return typeof value === 'boolean';
9919 function isPromiseLike(obj) {
9920 return obj && isFunction(obj.then);
9924 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
9925 function isTypedArray(value) {
9926 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
9929 function isArrayBuffer(obj) {
9930 return toString.call(obj) === '[object ArrayBuffer]';
9934 var trim = function(value) {
9935 return isString(value) ? value.trim() : value;
9939 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
9940 // Prereq: s is a string.
9941 var escapeForRegexp = function(s) {
9942 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
9943 replace(/\x08/g, '\\x08');
9949 * @name angular.isElement
9954 * Determines if a reference is a DOM element (or wrapped jQuery element).
9956 * @param {*} value Reference to check.
9957 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
9959 function isElement(node) {
9961 (node.nodeName // we are a direct element
9962 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
9966 * @param str 'key1,key2,...'
9967 * @returns {object} in the form of {key1:true, key2:true, ...}
9969 function makeMap(str) {
9970 var obj = {}, items = str.split(','), i;
9971 for (i = 0; i < items.length; i++) {
9972 obj[items[i]] = true;
9978 function nodeName_(element) {
9979 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
9982 function includes(array, obj) {
9983 return Array.prototype.indexOf.call(array, obj) != -1;
9986 function arrayRemove(array, value) {
9987 var index = array.indexOf(value);
9989 array.splice(index, 1);
9996 * @name angular.copy
10001 * Creates a deep copy of `source`, which should be an object or an array.
10003 * * If no destination is supplied, a copy of the object or array is created.
10004 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
10005 * are deleted and then all elements/properties from the source are copied to it.
10006 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
10007 * * If `source` is identical to 'destination' an exception will be thrown.
10009 * @param {*} source The source that will be used to make a copy.
10010 * Can be any type, including primitives, `null`, and `undefined`.
10011 * @param {(Object|Array)=} destination Destination into which the source is copied. If
10012 * provided, must be of the same type as `source`.
10013 * @returns {*} The copy or updated `destination`, if `destination` was specified.
10016 <example module="copyExample">
10017 <file name="index.html">
10018 <div ng-controller="ExampleController">
10019 <form novalidate class="simple-form">
10020 Name: <input type="text" ng-model="user.name" /><br />
10021 E-mail: <input type="email" ng-model="user.email" /><br />
10022 Gender: <input type="radio" ng-model="user.gender" value="male" />male
10023 <input type="radio" ng-model="user.gender" value="female" />female<br />
10024 <button ng-click="reset()">RESET</button>
10025 <button ng-click="update(user)">SAVE</button>
10027 <pre>form = {{user | json}}</pre>
10028 <pre>master = {{master | json}}</pre>
10032 angular.module('copyExample', [])
10033 .controller('ExampleController', ['$scope', function($scope) {
10036 $scope.update = function(user) {
10037 // Example with 1 argument
10038 $scope.master= angular.copy(user);
10041 $scope.reset = function() {
10042 // Example with 2 arguments
10043 angular.copy($scope.master, $scope.user);
10052 function copy(source, destination) {
10053 var stackSource = [];
10054 var stackDest = [];
10057 if (isTypedArray(destination) || isArrayBuffer(destination)) {
10058 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
10060 if (source === destination) {
10061 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
10064 // Empty the destination object
10065 if (isArray(destination)) {
10066 destination.length = 0;
10068 forEach(destination, function(value, key) {
10069 if (key !== '$$hashKey') {
10070 delete destination[key];
10075 stackSource.push(source);
10076 stackDest.push(destination);
10077 return copyRecurse(source, destination);
10080 return copyElement(source);
10082 function copyRecurse(source, destination) {
10083 var h = destination.$$hashKey;
10085 if (isArray(source)) {
10086 for (var i = 0, ii = source.length; i < ii; i++) {
10087 destination.push(copyElement(source[i]));
10089 } else if (isBlankObject(source)) {
10090 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
10091 for (key in source) {
10092 destination[key] = copyElement(source[key]);
10094 } else if (source && typeof source.hasOwnProperty === 'function') {
10095 // Slow path, which must rely on hasOwnProperty
10096 for (key in source) {
10097 if (source.hasOwnProperty(key)) {
10098 destination[key] = copyElement(source[key]);
10102 // Slowest path --- hasOwnProperty can't be called as a method
10103 for (key in source) {
10104 if (hasOwnProperty.call(source, key)) {
10105 destination[key] = copyElement(source[key]);
10109 setHashKey(destination, h);
10110 return destination;
10113 function copyElement(source) {
10115 if (!isObject(source)) {
10119 // Already copied values
10120 var index = stackSource.indexOf(source);
10121 if (index !== -1) {
10122 return stackDest[index];
10125 if (isWindow(source) || isScope(source)) {
10126 throw ngMinErr('cpws',
10127 "Can't copy! Making copies of Window or Scope instances is not supported.");
10130 var needsRecurse = false;
10131 var destination = copyType(source);
10133 if (destination === undefined) {
10134 destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
10135 needsRecurse = true;
10138 stackSource.push(source);
10139 stackDest.push(destination);
10141 return needsRecurse
10142 ? copyRecurse(source, destination)
10146 function copyType(source) {
10147 switch (toString.call(source)) {
10148 case '[object Int8Array]':
10149 case '[object Int16Array]':
10150 case '[object Int32Array]':
10151 case '[object Float32Array]':
10152 case '[object Float64Array]':
10153 case '[object Uint8Array]':
10154 case '[object Uint8ClampedArray]':
10155 case '[object Uint16Array]':
10156 case '[object Uint32Array]':
10157 return new source.constructor(copyElement(source.buffer));
10159 case '[object ArrayBuffer]':
10161 if (!source.slice) {
10162 var copied = new ArrayBuffer(source.byteLength);
10163 new Uint8Array(copied).set(new Uint8Array(source));
10166 return source.slice(0);
10168 case '[object Boolean]':
10169 case '[object Number]':
10170 case '[object String]':
10171 case '[object Date]':
10172 return new source.constructor(source.valueOf());
10174 case '[object RegExp]':
10175 var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
10176 re.lastIndex = source.lastIndex;
10179 case '[object Blob]':
10180 return new source.constructor([source], {type: source.type});
10183 if (isFunction(source.cloneNode)) {
10184 return source.cloneNode(true);
10190 * Creates a shallow copy of an object, an array or a primitive.
10192 * Assumes that there are no proto properties for objects.
10194 function shallowCopy(src, dst) {
10195 if (isArray(src)) {
10198 for (var i = 0, ii = src.length; i < ii; i++) {
10201 } else if (isObject(src)) {
10204 for (var key in src) {
10205 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
10206 dst[key] = src[key];
10217 * @name angular.equals
10222 * Determines if two objects or two values are equivalent. Supports value types, regular
10223 * expressions, arrays and objects.
10225 * Two objects or values are considered equivalent if at least one of the following is true:
10227 * * Both objects or values pass `===` comparison.
10228 * * Both objects or values are of the same type and all of their properties are equal by
10229 * comparing them with `angular.equals`.
10230 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
10231 * * Both values represent the same regular expression (In JavaScript,
10232 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
10233 * representation matches).
10235 * During a property comparison, properties of `function` type and properties with names
10236 * that begin with `$` are ignored.
10238 * Scope and DOMWindow objects are being compared only by identify (`===`).
10240 * @param {*} o1 Object or value to compare.
10241 * @param {*} o2 Object or value to compare.
10242 * @returns {boolean} True if arguments are equal.
10245 <example module="equalsExample" name="equalsExample">
10246 <file name="index.html">
10247 <div ng-controller="ExampleController">
10250 Name: <input type="text" ng-model="user1.name">
10251 Age: <input type="number" ng-model="user1.age">
10254 Name: <input type="text" ng-model="user2.name">
10255 Age: <input type="number" ng-model="user2.age">
10259 <input type="button" value="Compare" ng-click="compare()">
10261 User 1: <pre>{{user1 | json}}</pre>
10262 User 2: <pre>{{user2 | json}}</pre>
10263 Equal: <pre>{{result}}</pre>
10267 <file name="script.js">
10268 angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
10272 $scope.compare = function() {
10273 $scope.result = angular.equals($scope.user1, $scope.user2);
10279 function equals(o1, o2) {
10280 if (o1 === o2) return true;
10281 if (o1 === null || o2 === null) return false;
10282 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
10283 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
10284 if (t1 == t2 && t1 == 'object') {
10286 if (!isArray(o2)) return false;
10287 if ((length = o1.length) == o2.length) {
10288 for (key = 0; key < length; key++) {
10289 if (!equals(o1[key], o2[key])) return false;
10293 } else if (isDate(o1)) {
10294 if (!isDate(o2)) return false;
10295 return equals(o1.getTime(), o2.getTime());
10296 } else if (isRegExp(o1)) {
10297 if (!isRegExp(o2)) return false;
10298 return o1.toString() == o2.toString();
10300 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
10301 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
10302 keySet = createMap();
10304 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
10305 if (!equals(o1[key], o2[key])) return false;
10306 keySet[key] = true;
10309 if (!(key in keySet) &&
10310 key.charAt(0) !== '$' &&
10311 isDefined(o2[key]) &&
10312 !isFunction(o2[key])) return false;
10320 var csp = function() {
10321 if (!isDefined(csp.rules)) {
10324 var ngCspElement = (window.document.querySelector('[ng-csp]') ||
10325 window.document.querySelector('[data-ng-csp]'));
10327 if (ngCspElement) {
10328 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
10329 ngCspElement.getAttribute('data-ng-csp');
10331 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
10332 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
10336 noUnsafeEval: noUnsafeEval(),
10337 noInlineStyle: false
10344 function noUnsafeEval() {
10346 /* jshint -W031, -W054 */
10348 /* jshint +W031, +W054 */
10362 * @param {string=} ngJq the name of the library available under `window`
10363 * to be used for angular.element
10365 * Use this directive to force the angular.element library. This should be
10366 * used to force either jqLite by leaving ng-jq blank or setting the name of
10367 * the jquery variable under window (eg. jQuery).
10369 * Since angular looks for this directive when it is loaded (doesn't wait for the
10370 * DOMContentLoaded event), it must be placed on an element that comes before the script
10371 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
10375 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
10378 <html ng-app ng-jq>
10384 * This example shows how to use a jQuery based library of a different name.
10385 * The library name must be available at the top most 'window'.
10388 <html ng-app ng-jq="jQueryLib">
10394 var jq = function() {
10395 if (isDefined(jq.name_)) return jq.name_;
10397 var i, ii = ngAttrPrefixes.length, prefix, name;
10398 for (i = 0; i < ii; ++i) {
10399 prefix = ngAttrPrefixes[i];
10400 if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
10401 name = el.getAttribute(prefix + 'jq');
10406 return (jq.name_ = name);
10409 function concat(array1, array2, index) {
10410 return array1.concat(slice.call(array2, index));
10413 function sliceArgs(args, startIndex) {
10414 return slice.call(args, startIndex || 0);
10421 * @name angular.bind
10426 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
10427 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
10428 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
10429 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
10431 * @param {Object} self Context which `fn` should be evaluated in.
10432 * @param {function()} fn Function to be bound.
10433 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
10434 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
10437 function bind(self, fn) {
10438 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
10439 if (isFunction(fn) && !(fn instanceof RegExp)) {
10440 return curryArgs.length
10442 return arguments.length
10443 ? fn.apply(self, concat(curryArgs, arguments, 0))
10444 : fn.apply(self, curryArgs);
10447 return arguments.length
10448 ? fn.apply(self, arguments)
10452 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
10458 function toJsonReplacer(key, value) {
10461 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
10463 } else if (isWindow(value)) {
10465 } else if (value && window.document === value) {
10467 } else if (isScope(value)) {
10477 * @name angular.toJson
10482 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
10483 * stripped since angular uses this notation internally.
10485 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
10486 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
10487 * If set to an integer, the JSON output will contain that many spaces per indentation.
10488 * @returns {string|undefined} JSON-ified string representing `obj`.
10490 function toJson(obj, pretty) {
10491 if (isUndefined(obj)) return undefined;
10492 if (!isNumber(pretty)) {
10493 pretty = pretty ? 2 : null;
10495 return JSON.stringify(obj, toJsonReplacer, pretty);
10501 * @name angular.fromJson
10506 * Deserializes a JSON string.
10508 * @param {string} json JSON string to deserialize.
10509 * @returns {Object|Array|string|number} Deserialized JSON string.
10511 function fromJson(json) {
10512 return isString(json)
10518 var ALL_COLONS = /:/g;
10519 function timezoneToOffset(timezone, fallback) {
10520 // IE/Edge do not "understand" colon (`:`) in timezone
10521 timezone = timezone.replace(ALL_COLONS, '');
10522 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
10523 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
10527 function addDateMinutes(date, minutes) {
10528 date = new Date(date.getTime());
10529 date.setMinutes(date.getMinutes() + minutes);
10534 function convertTimezoneToLocal(date, timezone, reverse) {
10535 reverse = reverse ? -1 : 1;
10536 var dateTimezoneOffset = date.getTimezoneOffset();
10537 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
10538 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
10543 * @returns {string} Returns the string representation of the element.
10545 function startingTag(element) {
10546 element = jqLite(element).clone();
10548 // turns out IE does not let you set .html() on elements which
10549 // are not allowed to have children. So we just ignore it.
10552 var elemHtml = jqLite('<div>').append(element).html();
10554 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
10556 match(/^(<[^>]+>)/)[1].
10557 replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
10559 return lowercase(elemHtml);
10565 /////////////////////////////////////////////////
10568 * Tries to decode the URI component without throwing an exception.
10571 * @param str value potential URI component to check.
10572 * @returns {boolean} True if `value` can be decoded
10573 * with the decodeURIComponent function.
10575 function tryDecodeURIComponent(value) {
10577 return decodeURIComponent(value);
10579 // Ignore any invalid uri component
10585 * Parses an escaped url query string into key-value pairs.
10586 * @returns {Object.<string,boolean|Array>}
10588 function parseKeyValue(/**string*/keyValue) {
10590 forEach((keyValue || "").split('&'), function(keyValue) {
10591 var splitPoint, key, val;
10593 key = keyValue = keyValue.replace(/\+/g,'%20');
10594 splitPoint = keyValue.indexOf('=');
10595 if (splitPoint !== -1) {
10596 key = keyValue.substring(0, splitPoint);
10597 val = keyValue.substring(splitPoint + 1);
10599 key = tryDecodeURIComponent(key);
10600 if (isDefined(key)) {
10601 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
10602 if (!hasOwnProperty.call(obj, key)) {
10604 } else if (isArray(obj[key])) {
10605 obj[key].push(val);
10607 obj[key] = [obj[key],val];
10615 function toKeyValue(obj) {
10617 forEach(obj, function(value, key) {
10618 if (isArray(value)) {
10619 forEach(value, function(arrayValue) {
10620 parts.push(encodeUriQuery(key, true) +
10621 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
10624 parts.push(encodeUriQuery(key, true) +
10625 (value === true ? '' : '=' + encodeUriQuery(value, true)));
10628 return parts.length ? parts.join('&') : '';
10633 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
10634 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
10637 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
10638 * pct-encoded = "%" HEXDIG HEXDIG
10639 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
10640 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
10641 * / "*" / "+" / "," / ";" / "="
10643 function encodeUriSegment(val) {
10644 return encodeUriQuery(val, true).
10645 replace(/%26/gi, '&').
10646 replace(/%3D/gi, '=').
10647 replace(/%2B/gi, '+');
10652 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
10653 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
10654 * encoded per http://tools.ietf.org/html/rfc3986:
10655 * query = *( pchar / "/" / "?" )
10656 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
10657 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
10658 * pct-encoded = "%" HEXDIG HEXDIG
10659 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
10660 * / "*" / "+" / "," / ";" / "="
10662 function encodeUriQuery(val, pctEncodeSpaces) {
10663 return encodeURIComponent(val).
10664 replace(/%40/gi, '@').
10665 replace(/%3A/gi, ':').
10666 replace(/%24/g, '$').
10667 replace(/%2C/gi, ',').
10668 replace(/%3B/gi, ';').
10669 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
10672 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
10674 function getNgAttribute(element, ngAttr) {
10675 var attr, i, ii = ngAttrPrefixes.length;
10676 for (i = 0; i < ii; ++i) {
10677 attr = ngAttrPrefixes[i] + ngAttr;
10678 if (isString(attr = element.getAttribute(attr))) {
10691 * @param {angular.Module} ngApp an optional application
10692 * {@link angular.module module} name to load.
10693 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
10694 * created in "strict-di" mode. This means that the application will fail to invoke functions which
10695 * do not use explicit function annotation (and are thus unsuitable for minification), as described
10696 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
10697 * tracking down the root of these bugs.
10701 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
10702 * designates the **root element** of the application and is typically placed near the root element
10703 * of the page - e.g. on the `<body>` or `<html>` tags.
10705 * There are a few things to keep in mind when using `ngApp`:
10706 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
10707 * found in the document will be used to define the root element to auto-bootstrap as an
10708 * application. To run multiple applications in an HTML document you must manually bootstrap them using
10709 * {@link angular.bootstrap} instead.
10710 * - AngularJS applications cannot be nested within each other.
10711 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
10712 * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
10713 * {@link ngRoute.ngView `ngView`}.
10714 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
10715 * causing animations to stop working and making the injector inaccessible from outside the app.
10717 * You can specify an **AngularJS module** to be used as the root module for the application. This
10718 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
10719 * should contain the application code needed or have dependencies on other modules that will
10720 * contain the code. See {@link angular.module} for more information.
10722 * In the example below if the `ngApp` directive were not placed on the `html` element then the
10723 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
10724 * would not be resolved to `3`.
10726 * `ngApp` is the easiest, and most common way to bootstrap an application.
10728 <example module="ngAppDemo">
10729 <file name="index.html">
10730 <div ng-controller="ngAppDemoController">
10731 I can add: {{a}} + {{b}} = {{ a+b }}
10734 <file name="script.js">
10735 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
10742 * Using `ngStrictDi`, you would see something like this:
10744 <example ng-app-included="true">
10745 <file name="index.html">
10746 <div ng-app="ngAppStrictDemo" ng-strict-di>
10747 <div ng-controller="GoodController1">
10748 I can add: {{a}} + {{b}} = {{ a+b }}
10750 <p>This renders because the controller does not fail to
10751 instantiate, by using explicit annotation style (see
10752 script.js for details)
10756 <div ng-controller="GoodController2">
10757 Name: <input ng-model="name"><br />
10760 <p>This renders because the controller does not fail to
10761 instantiate, by using explicit annotation style
10762 (see script.js for details)
10766 <div ng-controller="BadController">
10767 I can add: {{a}} + {{b}} = {{ a+b }}
10769 <p>The controller could not be instantiated, due to relying
10770 on automatic function annotations (which are disabled in
10771 strict mode). As such, the content of this section is not
10772 interpolated, and there should be an error in your web console.
10777 <file name="script.js">
10778 angular.module('ngAppStrictDemo', [])
10779 // BadController will fail to instantiate, due to relying on automatic function annotation,
10780 // rather than an explicit annotation
10781 .controller('BadController', function($scope) {
10785 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
10786 // due to using explicit annotations using the array style and $inject property, respectively.
10787 .controller('GoodController1', ['$scope', function($scope) {
10791 .controller('GoodController2', GoodController2);
10792 function GoodController2($scope) {
10793 $scope.name = "World";
10795 GoodController2.$inject = ['$scope'];
10797 <file name="style.css">
10798 div[ng-controller] {
10799 margin-bottom: 1em;
10800 -webkit-border-radius: 4px;
10801 border-radius: 4px;
10805 div[ng-controller^=Good] {
10806 border-color: #d6e9c6;
10807 background-color: #dff0d8;
10810 div[ng-controller^=Bad] {
10811 border-color: #ebccd1;
10812 background-color: #f2dede;
10819 function angularInit(element, bootstrap) {
10824 // The element `element` has priority over any other element
10825 forEach(ngAttrPrefixes, function(prefix) {
10826 var name = prefix + 'app';
10828 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
10829 appElement = element;
10830 module = element.getAttribute(name);
10833 forEach(ngAttrPrefixes, function(prefix) {
10834 var name = prefix + 'app';
10837 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
10838 appElement = candidate;
10839 module = candidate.getAttribute(name);
10843 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
10844 bootstrap(appElement, module ? [module] : [], config);
10850 * @name angular.bootstrap
10853 * Use this function to manually start up angular application.
10855 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
10857 * Angular will detect if it has been loaded into the browser more than once and only allow the
10858 * first loaded script to be bootstrapped and will report a warning to the browser console for
10859 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
10860 * multiple instances of Angular try to work on the DOM.
10862 * <div class="alert alert-warning">
10863 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
10864 * They must use {@link ng.directive:ngApp ngApp}.
10867 * <div class="alert alert-warning">
10868 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
10869 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
10870 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
10871 * causing animations to stop working and making the injector inaccessible from outside the app.
10878 * <div ng-controller="WelcomeController">
10882 * <script src="angular.js"></script>
10884 * var app = angular.module('demo', [])
10885 * .controller('WelcomeController', function($scope) {
10886 * $scope.greeting = 'Welcome!';
10888 * angular.bootstrap(document, ['demo']);
10894 * @param {DOMElement} element DOM element which is the root of angular application.
10895 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
10896 * Each item in the array should be the name of a predefined module or a (DI annotated)
10897 * function that will be invoked by the injector as a `config` block.
10898 * See: {@link angular.module modules}
10899 * @param {Object=} config an object for defining configuration options for the application. The
10900 * following keys are supported:
10902 * * `strictDi` - disable automatic function annotation for the application. This is meant to
10903 * assist in finding bugs which break minified code. Defaults to `false`.
10905 * @returns {auto.$injector} Returns the newly created injector for this app.
10907 function bootstrap(element, modules, config) {
10908 if (!isObject(config)) config = {};
10909 var defaultConfig = {
10912 config = extend(defaultConfig, config);
10913 var doBootstrap = function() {
10914 element = jqLite(element);
10916 if (element.injector()) {
10917 var tag = (element[0] === window.document) ? 'document' : startingTag(element);
10918 //Encode angle brackets to prevent input from being sanitized to empty string #8683
10921 "App already bootstrapped with this element '{0}'",
10922 tag.replace(/</,'<').replace(/>/,'>'));
10925 modules = modules || [];
10926 modules.unshift(['$provide', function($provide) {
10927 $provide.value('$rootElement', element);
10930 if (config.debugInfoEnabled) {
10931 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
10932 modules.push(['$compileProvider', function($compileProvider) {
10933 $compileProvider.debugInfoEnabled(true);
10937 modules.unshift('ng');
10938 var injector = createInjector(modules, config.strictDi);
10939 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
10940 function bootstrapApply(scope, element, compile, injector) {
10941 scope.$apply(function() {
10942 element.data('$injector', injector);
10943 compile(element)(scope);
10950 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
10951 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
10953 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
10954 config.debugInfoEnabled = true;
10955 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
10958 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
10959 return doBootstrap();
10962 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
10963 angular.resumeBootstrap = function(extraModules) {
10964 forEach(extraModules, function(module) {
10965 modules.push(module);
10967 return doBootstrap();
10970 if (isFunction(angular.resumeDeferredBootstrap)) {
10971 angular.resumeDeferredBootstrap();
10977 * @name angular.reloadWithDebugInfo
10980 * Use this function to reload the current application with debug information turned on.
10981 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
10983 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
10985 function reloadWithDebugInfo() {
10986 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
10987 window.location.reload();
10991 * @name angular.getTestability
10994 * Get the testability service for the instance of Angular on the given
10996 * @param {DOMElement} element DOM element which is the root of angular application.
10998 function getTestability(rootElement) {
10999 var injector = angular.element(rootElement).injector();
11001 throw ngMinErr('test',
11002 'no injector found for element argument to getTestability');
11004 return injector.get('$$testability');
11007 var SNAKE_CASE_REGEXP = /[A-Z]/g;
11008 function snake_case(name, separator) {
11009 separator = separator || '_';
11010 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
11011 return (pos ? separator : '') + letter.toLowerCase();
11015 var bindJQueryFired = false;
11016 function bindJQuery() {
11017 var originalCleanData;
11019 if (bindJQueryFired) {
11023 // bind to jQuery if present;
11025 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
11026 !jqName ? undefined : // use jqLite
11027 window[jqName]; // use jQuery specified by `ngJq`
11029 // Use jQuery if it exists with proper functionality, otherwise default to us.
11030 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
11031 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
11032 // versions. It will not work for sure with jQuery <1.7, though.
11033 if (jQuery && jQuery.fn.on) {
11035 extend(jQuery.fn, {
11036 scope: JQLitePrototype.scope,
11037 isolateScope: JQLitePrototype.isolateScope,
11038 controller: JQLitePrototype.controller,
11039 injector: JQLitePrototype.injector,
11040 inheritedData: JQLitePrototype.inheritedData
11043 // All nodes removed from the DOM via various jQuery APIs like .remove()
11044 // are passed through jQuery.cleanData. Monkey-patch this method to fire
11045 // the $destroy event on all removed nodes.
11046 originalCleanData = jQuery.cleanData;
11047 jQuery.cleanData = function(elems) {
11049 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
11050 events = jQuery._data(elem, "events");
11051 if (events && events.$destroy) {
11052 jQuery(elem).triggerHandler('$destroy');
11055 originalCleanData(elems);
11061 angular.element = jqLite;
11063 // Prevent double-proxying.
11064 bindJQueryFired = true;
11068 * throw error if the argument is falsy.
11070 function assertArg(arg, name, reason) {
11072 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
11077 function assertArgFn(arg, name, acceptArrayAnnotation) {
11078 if (acceptArrayAnnotation && isArray(arg)) {
11079 arg = arg[arg.length - 1];
11082 assertArg(isFunction(arg), name, 'not a function, got ' +
11083 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
11088 * throw error if the name given is hasOwnProperty
11089 * @param {String} name the name to test
11090 * @param {String} context the context in which the name is used, such as module or directive
11092 function assertNotHasOwnProperty(name, context) {
11093 if (name === 'hasOwnProperty') {
11094 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
11099 * Return the value accessible from the object by path. Any undefined traversals are ignored
11100 * @param {Object} obj starting object
11101 * @param {String} path path to traverse
11102 * @param {boolean} [bindFnToScope=true]
11103 * @returns {Object} value as accessible by path
11105 //TODO(misko): this function needs to be removed
11106 function getter(obj, path, bindFnToScope) {
11107 if (!path) return obj;
11108 var keys = path.split('.');
11110 var lastInstance = obj;
11111 var len = keys.length;
11113 for (var i = 0; i < len; i++) {
11116 obj = (lastInstance = obj)[key];
11119 if (!bindFnToScope && isFunction(obj)) {
11120 return bind(lastInstance, obj);
11126 * Return the DOM siblings between the first and last node in the given array.
11127 * @param {Array} array like object
11128 * @returns {Array} the inputted object or a jqLite collection containing the nodes
11130 function getBlockNodes(nodes) {
11131 // TODO(perf): update `nodes` instead of creating a new object?
11132 var node = nodes[0];
11133 var endNode = nodes[nodes.length - 1];
11136 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
11137 if (blockNodes || nodes[i] !== node) {
11139 blockNodes = jqLite(slice.call(nodes, 0, i));
11141 blockNodes.push(node);
11145 return blockNodes || nodes;
11150 * Creates a new object without a prototype. This object is useful for lookup without having to
11151 * guard against prototypically inherited properties via hasOwnProperty.
11153 * Related micro-benchmarks:
11154 * - http://jsperf.com/object-create2
11155 * - http://jsperf.com/proto-map-lookup/2
11156 * - http://jsperf.com/for-in-vs-object-keys2
11158 * @returns {Object}
11160 function createMap() {
11161 return Object.create(null);
11164 var NODE_TYPE_ELEMENT = 1;
11165 var NODE_TYPE_ATTRIBUTE = 2;
11166 var NODE_TYPE_TEXT = 3;
11167 var NODE_TYPE_COMMENT = 8;
11168 var NODE_TYPE_DOCUMENT = 9;
11169 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
11173 * @name angular.Module
11177 * Interface for configuring angular {@link angular.module modules}.
11180 function setupModuleLoader(window) {
11182 var $injectorMinErr = minErr('$injector');
11183 var ngMinErr = minErr('ng');
11185 function ensure(obj, name, factory) {
11186 return obj[name] || (obj[name] = factory());
11189 var angular = ensure(window, 'angular', Object);
11191 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
11192 angular.$$minErr = angular.$$minErr || minErr;
11194 return ensure(angular, 'module', function() {
11195 /** @type {Object.<string, angular.Module>} */
11200 * @name angular.module
11204 * The `angular.module` is a global place for creating, registering and retrieving Angular
11206 * All modules (angular core or 3rd party) that should be available to an application must be
11207 * registered using this mechanism.
11209 * Passing one argument retrieves an existing {@link angular.Module},
11210 * whereas passing more than one argument creates a new {@link angular.Module}
11215 * A module is a collection of services, directives, controllers, filters, and configuration information.
11216 * `angular.module` is used to configure the {@link auto.$injector $injector}.
11219 * // Create a new module
11220 * var myModule = angular.module('myModule', []);
11222 * // register a new service
11223 * myModule.value('appName', 'MyCoolApp');
11225 * // configure existing services inside initialization blocks.
11226 * myModule.config(['$locationProvider', function($locationProvider) {
11227 * // Configure existing providers
11228 * $locationProvider.hashPrefix('!');
11232 * Then you can create an injector and load your modules like this:
11235 * var injector = angular.injector(['ng', 'myModule'])
11238 * However it's more likely that you'll just use
11239 * {@link ng.directive:ngApp ngApp} or
11240 * {@link angular.bootstrap} to simplify this process for you.
11242 * @param {!string} name The name of the module to create or retrieve.
11243 * @param {!Array.<string>=} requires If specified then new module is being created. If
11244 * unspecified then the module is being retrieved for further configuration.
11245 * @param {Function=} configFn Optional configuration function for the module. Same as
11246 * {@link angular.Module#config Module#config()}.
11247 * @returns {angular.Module} new module with the {@link angular.Module} api.
11249 return function module(name, requires, configFn) {
11250 var assertNotHasOwnProperty = function(name, context) {
11251 if (name === 'hasOwnProperty') {
11252 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
11256 assertNotHasOwnProperty(name, 'module');
11257 if (requires && modules.hasOwnProperty(name)) {
11258 modules[name] = null;
11260 return ensure(modules, name, function() {
11262 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
11263 "the module name or forgot to load it. If registering a module ensure that you " +
11264 "specify the dependencies as the second argument.", name);
11267 /** @type {!Array.<Array.<*>>} */
11268 var invokeQueue = [];
11270 /** @type {!Array.<Function>} */
11271 var configBlocks = [];
11273 /** @type {!Array.<Function>} */
11274 var runBlocks = [];
11276 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
11278 /** @type {angular.Module} */
11279 var moduleInstance = {
11281 _invokeQueue: invokeQueue,
11282 _configBlocks: configBlocks,
11283 _runBlocks: runBlocks,
11287 * @name angular.Module#requires
11291 * Holds the list of modules which the injector will load before the current module is
11294 requires: requires,
11298 * @name angular.Module#name
11302 * Name of the module.
11309 * @name angular.Module#provider
11311 * @param {string} name service name
11312 * @param {Function} providerType Construction function for creating new instance of the
11315 * See {@link auto.$provide#provider $provide.provider()}.
11317 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
11321 * @name angular.Module#factory
11323 * @param {string} name service name
11324 * @param {Function} providerFunction Function for creating new instance of the service.
11326 * See {@link auto.$provide#factory $provide.factory()}.
11328 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
11332 * @name angular.Module#service
11334 * @param {string} name service name
11335 * @param {Function} constructor A constructor function that will be instantiated.
11337 * See {@link auto.$provide#service $provide.service()}.
11339 service: invokeLaterAndSetModuleName('$provide', 'service'),
11343 * @name angular.Module#value
11345 * @param {string} name service name
11346 * @param {*} object Service instance object.
11348 * See {@link auto.$provide#value $provide.value()}.
11350 value: invokeLater('$provide', 'value'),
11354 * @name angular.Module#constant
11356 * @param {string} name constant name
11357 * @param {*} object Constant value.
11359 * Because the constants are fixed, they get applied before other provide methods.
11360 * See {@link auto.$provide#constant $provide.constant()}.
11362 constant: invokeLater('$provide', 'constant', 'unshift'),
11366 * @name angular.Module#decorator
11368 * @param {string} name The name of the service to decorate.
11369 * @param {Function} decorFn This function will be invoked when the service needs to be
11370 * instantiated and should return the decorated service instance.
11372 * See {@link auto.$provide#decorator $provide.decorator()}.
11374 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
11378 * @name angular.Module#animation
11380 * @param {string} name animation name
11381 * @param {Function} animationFactory Factory function for creating new instance of an
11385 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
11388 * Defines an animation hook that can be later used with
11389 * {@link $animate $animate} service and directives that use this service.
11392 * module.animation('.animation-name', function($inject1, $inject2) {
11394 * eventName : function(element, done) {
11395 * //code to run the animation
11396 * //once complete, then run done()
11397 * return function cancellationFunction(element) {
11398 * //code to cancel the animation
11405 * See {@link ng.$animateProvider#register $animateProvider.register()} and
11406 * {@link ngAnimate ngAnimate module} for more information.
11408 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
11412 * @name angular.Module#filter
11414 * @param {string} name Filter name - this must be a valid angular expression identifier
11415 * @param {Function} filterFactory Factory function for creating new instance of filter.
11417 * See {@link ng.$filterProvider#register $filterProvider.register()}.
11419 * <div class="alert alert-warning">
11420 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
11421 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
11422 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
11423 * (`myapp_subsection_filterx`).
11426 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
11430 * @name angular.Module#controller
11432 * @param {string|Object} name Controller name, or an object map of controllers where the
11433 * keys are the names and the values are the constructors.
11434 * @param {Function} constructor Controller constructor function.
11436 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
11438 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
11442 * @name angular.Module#directive
11444 * @param {string|Object} name Directive name, or an object map of directives where the
11445 * keys are the names and the values are the factories.
11446 * @param {Function} directiveFactory Factory function for creating new instance of
11449 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
11451 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
11455 * @name angular.Module#component
11457 * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
11458 * @param {Object} options Component definition object (a simplified
11459 * {@link ng.$compile#directive-definition-object directive definition object})
11462 * See {@link ng.$compileProvider#component $compileProvider.component()}.
11464 component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
11468 * @name angular.Module#config
11470 * @param {Function} configFn Execute this function on module load. Useful for service
11473 * Use this method to register work which needs to be performed on module loading.
11474 * For more about how to configure services, see
11475 * {@link providers#provider-recipe Provider Recipe}.
11481 * @name angular.Module#run
11483 * @param {Function} initializationFn Execute this function after injector creation.
11484 * Useful for application initialization.
11486 * Use this method to register work which should be performed when the injector is done
11487 * loading all modules.
11489 run: function(block) {
11490 runBlocks.push(block);
11499 return moduleInstance;
11502 * @param {string} provider
11503 * @param {string} method
11504 * @param {String=} insertMethod
11505 * @returns {angular.Module}
11507 function invokeLater(provider, method, insertMethod, queue) {
11508 if (!queue) queue = invokeQueue;
11509 return function() {
11510 queue[insertMethod || 'push']([provider, method, arguments]);
11511 return moduleInstance;
11516 * @param {string} provider
11517 * @param {string} method
11518 * @returns {angular.Module}
11520 function invokeLaterAndSetModuleName(provider, method) {
11521 return function(recipeName, factoryFunction) {
11522 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
11523 invokeQueue.push([provider, method, arguments]);
11524 return moduleInstance;
11533 /* global: toDebugString: true */
11535 function serializeObject(obj) {
11538 return JSON.stringify(obj, function(key, val) {
11539 val = toJsonReplacer(key, val);
11540 if (isObject(val)) {
11542 if (seen.indexOf(val) >= 0) return '...';
11550 function toDebugString(obj) {
11551 if (typeof obj === 'function') {
11552 return obj.toString().replace(/ \{[\s\S]*$/, '');
11553 } else if (isUndefined(obj)) {
11554 return 'undefined';
11555 } else if (typeof obj !== 'string') {
11556 return serializeObject(obj);
11561 /* global angularModule: true,
11566 htmlAnchorDirective,
11575 ngBindHtmlDirective,
11576 ngBindTemplateDirective,
11578 ngClassEvenDirective,
11579 ngClassOddDirective,
11581 ngControllerDirective,
11585 ngIncludeDirective,
11586 ngIncludeFillContentDirective,
11588 ngNonBindableDirective,
11589 ngPluralizeDirective,
11594 ngSwitchWhenDirective,
11595 ngSwitchDefaultDirective,
11596 ngOptionsDirective,
11597 ngTranscludeDirective,
11605 minlengthDirective,
11606 minlengthDirective,
11607 maxlengthDirective,
11608 maxlengthDirective,
11610 ngModelOptionsDirective,
11611 ngAttributeAliasDirectives,
11614 $AnchorScrollProvider,
11616 $CoreAnimateCssProvider,
11617 $$CoreAnimateJsProvider,
11618 $$CoreAnimateQueueProvider,
11619 $$AnimateRunnerFactoryProvider,
11620 $$AnimateAsyncRunFactoryProvider,
11622 $CacheFactoryProvider,
11623 $ControllerProvider,
11626 $ExceptionHandlerProvider,
11628 $$ForceReflowProvider,
11629 $InterpolateProvider,
11633 $HttpParamSerializerProvider,
11634 $HttpParamSerializerJQLikeProvider,
11635 $HttpBackendProvider,
11636 $xhrFactoryProvider,
11640 $RootScopeProvider,
11643 $$SanitizeUriProvider,
11645 $SceDelegateProvider,
11647 $TemplateCacheProvider,
11648 $TemplateRequestProvider,
11649 $$TestabilityProvider,
11654 $$CookieReaderProvider
11660 * @name angular.version
11663 * An object that contains information about the current AngularJS version.
11665 * This object has the following properties:
11667 * - `full` – `{string}` – Full version string, such as "0.9.18".
11668 * - `major` – `{number}` – Major version number, such as "0".
11669 * - `minor` – `{number}` – Minor version number, such as "9".
11670 * - `dot` – `{number}` – Dot version number, such as "18".
11671 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
11674 full: '1.5.5', // all of these placeholder strings will be replaced by grunt's
11675 major: 1, // package task
11678 codeName: 'material-conspiration'
11682 function publishExternalAPI(angular) {
11684 'bootstrap': bootstrap,
11690 'forEach': forEach,
11691 'injector': createInjector,
11695 'fromJson': fromJson,
11696 'identity': identity,
11697 'isUndefined': isUndefined,
11698 'isDefined': isDefined,
11699 'isString': isString,
11700 'isFunction': isFunction,
11701 'isObject': isObject,
11702 'isNumber': isNumber,
11703 'isElement': isElement,
11704 'isArray': isArray,
11705 'version': version,
11707 'lowercase': lowercase,
11708 'uppercase': uppercase,
11709 'callbacks': {counter: 0},
11710 'getTestability': getTestability,
11711 '$$minErr': minErr,
11713 'reloadWithDebugInfo': reloadWithDebugInfo
11716 angularModule = setupModuleLoader(window);
11718 angularModule('ng', ['ngLocale'], ['$provide',
11719 function ngModule($provide) {
11720 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
11721 $provide.provider({
11722 $$sanitizeUri: $$SanitizeUriProvider
11724 $provide.provider('$compile', $CompileProvider).
11726 a: htmlAnchorDirective,
11727 input: inputDirective,
11728 textarea: inputDirective,
11729 form: formDirective,
11730 script: scriptDirective,
11731 select: selectDirective,
11732 style: styleDirective,
11733 option: optionDirective,
11734 ngBind: ngBindDirective,
11735 ngBindHtml: ngBindHtmlDirective,
11736 ngBindTemplate: ngBindTemplateDirective,
11737 ngClass: ngClassDirective,
11738 ngClassEven: ngClassEvenDirective,
11739 ngClassOdd: ngClassOddDirective,
11740 ngCloak: ngCloakDirective,
11741 ngController: ngControllerDirective,
11742 ngForm: ngFormDirective,
11743 ngHide: ngHideDirective,
11744 ngIf: ngIfDirective,
11745 ngInclude: ngIncludeDirective,
11746 ngInit: ngInitDirective,
11747 ngNonBindable: ngNonBindableDirective,
11748 ngPluralize: ngPluralizeDirective,
11749 ngRepeat: ngRepeatDirective,
11750 ngShow: ngShowDirective,
11751 ngStyle: ngStyleDirective,
11752 ngSwitch: ngSwitchDirective,
11753 ngSwitchWhen: ngSwitchWhenDirective,
11754 ngSwitchDefault: ngSwitchDefaultDirective,
11755 ngOptions: ngOptionsDirective,
11756 ngTransclude: ngTranscludeDirective,
11757 ngModel: ngModelDirective,
11758 ngList: ngListDirective,
11759 ngChange: ngChangeDirective,
11760 pattern: patternDirective,
11761 ngPattern: patternDirective,
11762 required: requiredDirective,
11763 ngRequired: requiredDirective,
11764 minlength: minlengthDirective,
11765 ngMinlength: minlengthDirective,
11766 maxlength: maxlengthDirective,
11767 ngMaxlength: maxlengthDirective,
11768 ngValue: ngValueDirective,
11769 ngModelOptions: ngModelOptionsDirective
11772 ngInclude: ngIncludeFillContentDirective
11774 directive(ngAttributeAliasDirectives).
11775 directive(ngEventDirectives);
11776 $provide.provider({
11777 $anchorScroll: $AnchorScrollProvider,
11778 $animate: $AnimateProvider,
11779 $animateCss: $CoreAnimateCssProvider,
11780 $$animateJs: $$CoreAnimateJsProvider,
11781 $$animateQueue: $$CoreAnimateQueueProvider,
11782 $$AnimateRunner: $$AnimateRunnerFactoryProvider,
11783 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
11784 $browser: $BrowserProvider,
11785 $cacheFactory: $CacheFactoryProvider,
11786 $controller: $ControllerProvider,
11787 $document: $DocumentProvider,
11788 $exceptionHandler: $ExceptionHandlerProvider,
11789 $filter: $FilterProvider,
11790 $$forceReflow: $$ForceReflowProvider,
11791 $interpolate: $InterpolateProvider,
11792 $interval: $IntervalProvider,
11793 $http: $HttpProvider,
11794 $httpParamSerializer: $HttpParamSerializerProvider,
11795 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
11796 $httpBackend: $HttpBackendProvider,
11797 $xhrFactory: $xhrFactoryProvider,
11798 $location: $LocationProvider,
11799 $log: $LogProvider,
11800 $parse: $ParseProvider,
11801 $rootScope: $RootScopeProvider,
11804 $sce: $SceProvider,
11805 $sceDelegate: $SceDelegateProvider,
11806 $sniffer: $SnifferProvider,
11807 $templateCache: $TemplateCacheProvider,
11808 $templateRequest: $TemplateRequestProvider,
11809 $$testability: $$TestabilityProvider,
11810 $timeout: $TimeoutProvider,
11811 $window: $WindowProvider,
11812 $$rAF: $$RAFProvider,
11813 $$jqLite: $$jqLiteProvider,
11814 $$HashMap: $$HashMapProvider,
11815 $$cookieReader: $$CookieReaderProvider
11821 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
11822 * Any commits to this file should be reviewed with security in mind. *
11823 * Changes to this file can potentially create security vulnerabilities. *
11824 * An approval from 2 Core members with history of modifying *
11825 * this file is required. *
11827 * Does the change somehow allow for arbitrary javascript to be executed? *
11828 * Or allows for someone to change the prototype of built-in objects? *
11829 * Or gives undesired access to variables likes document or window? *
11830 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
11832 /* global JQLitePrototype: true,
11833 addEventListenerFn: true,
11834 removeEventListenerFn: true,
11835 BOOLEAN_ATTR: true,
11836 ALIASED_ATTR: true,
11839 //////////////////////////////////
11841 //////////////////////////////////
11845 * @name angular.element
11850 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
11852 * If jQuery is available, `angular.element` is an alias for the
11853 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
11854 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
11856 * jqLite is a tiny, API-compatible subset of jQuery that allows
11857 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
11858 * commonly needed functionality with the goal of having a very small footprint.
11860 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
11861 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
11862 * specific version of jQuery if multiple versions exist on the page.
11864 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
11865 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
11867 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
11868 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
11869 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
11871 * ## Angular's jqLite
11872 * jqLite provides only the following jQuery methods:
11874 * - [`addClass()`](http://api.jquery.com/addClass/)
11875 * - [`after()`](http://api.jquery.com/after/)
11876 * - [`append()`](http://api.jquery.com/append/)
11877 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
11878 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
11879 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
11880 * - [`clone()`](http://api.jquery.com/clone/)
11881 * - [`contents()`](http://api.jquery.com/contents/)
11882 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
11883 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
11884 * - [`data()`](http://api.jquery.com/data/)
11885 * - [`detach()`](http://api.jquery.com/detach/)
11886 * - [`empty()`](http://api.jquery.com/empty/)
11887 * - [`eq()`](http://api.jquery.com/eq/)
11888 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
11889 * - [`hasClass()`](http://api.jquery.com/hasClass/)
11890 * - [`html()`](http://api.jquery.com/html/)
11891 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
11892 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
11893 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
11894 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
11895 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
11896 * - [`prepend()`](http://api.jquery.com/prepend/)
11897 * - [`prop()`](http://api.jquery.com/prop/)
11898 * - [`ready()`](http://api.jquery.com/ready/)
11899 * - [`remove()`](http://api.jquery.com/remove/)
11900 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
11901 * - [`removeClass()`](http://api.jquery.com/removeClass/)
11902 * - [`removeData()`](http://api.jquery.com/removeData/)
11903 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
11904 * - [`text()`](http://api.jquery.com/text/)
11905 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
11906 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
11907 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
11908 * - [`val()`](http://api.jquery.com/val/)
11909 * - [`wrap()`](http://api.jquery.com/wrap/)
11911 * ## jQuery/jqLite Extras
11912 * Angular also provides the following additional methods and events to both jQuery and jqLite:
11915 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
11916 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
11917 * element before it is removed.
11920 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
11921 * retrieves controller associated with the `ngController` directive. If `name` is provided as
11922 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
11924 * - `injector()` - retrieves the injector of the current element or its parent.
11925 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
11926 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
11928 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
11929 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
11930 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
11931 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
11932 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
11933 * parent element is reached.
11935 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
11936 * https://github.com/angular/angular.js/issues/14251 for more information.
11938 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
11939 * @returns {Object} jQuery object.
11942 JQLite.expando = 'ng339';
11944 var jqCache = JQLite.cache = {},
11946 addEventListenerFn = function(element, type, fn) {
11947 element.addEventListener(type, fn, false);
11949 removeEventListenerFn = function(element, type, fn) {
11950 element.removeEventListener(type, fn, false);
11954 * !!! This is an undocumented "private" function !!!
11956 JQLite._data = function(node) {
11957 //jQuery always returns an object on cache miss
11958 return this.cache[node[this.expando]] || {};
11961 function jqNextId() { return ++jqId; }
11964 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
11965 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
11966 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
11967 var jqLiteMinErr = minErr('jqLite');
11970 * Converts snake_case to camelCase.
11971 * Also there is special case for Moz prefix starting with upper case letter.
11972 * @param name Name to normalize
11974 function camelCase(name) {
11976 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
11977 return offset ? letter.toUpperCase() : letter;
11979 replace(MOZ_HACK_REGEXP, 'Moz$1');
11982 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
11983 var HTML_REGEXP = /<|&#?\w+;/;
11984 var TAG_NAME_REGEXP = /<([\w:-]+)/;
11985 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
11988 'option': [1, '<select multiple="multiple">', '</select>'],
11990 'thead': [1, '<table>', '</table>'],
11991 'col': [2, '<table><colgroup>', '</colgroup></table>'],
11992 'tr': [2, '<table><tbody>', '</tbody></table>'],
11993 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
11994 '_default': [0, "", ""]
11997 wrapMap.optgroup = wrapMap.option;
11998 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
11999 wrapMap.th = wrapMap.td;
12002 function jqLiteIsTextNode(html) {
12003 return !HTML_REGEXP.test(html);
12006 function jqLiteAcceptsData(node) {
12007 // The window object can accept data but has no nodeType
12008 // Otherwise we are only interested in elements (1) and documents (9)
12009 var nodeType = node.nodeType;
12010 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
12013 function jqLiteHasData(node) {
12014 for (var key in jqCache[node.ng339]) {
12020 function jqLiteCleanData(nodes) {
12021 for (var i = 0, ii = nodes.length; i < ii; i++) {
12022 jqLiteRemoveData(nodes[i]);
12026 function jqLiteBuildFragment(html, context) {
12027 var tmp, tag, wrap,
12028 fragment = context.createDocumentFragment(),
12031 if (jqLiteIsTextNode(html)) {
12032 // Convert non-html into a text node
12033 nodes.push(context.createTextNode(html));
12035 // Convert html into DOM nodes
12036 tmp = tmp || fragment.appendChild(context.createElement("div"));
12037 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
12038 wrap = wrapMap[tag] || wrapMap._default;
12039 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
12041 // Descend through wrappers to the right content
12044 tmp = tmp.lastChild;
12047 nodes = concat(nodes, tmp.childNodes);
12049 tmp = fragment.firstChild;
12050 tmp.textContent = "";
12053 // Remove wrapper from fragment
12054 fragment.textContent = "";
12055 fragment.innerHTML = ""; // Clear inner HTML
12056 forEach(nodes, function(node) {
12057 fragment.appendChild(node);
12063 function jqLiteParseHTML(html, context) {
12064 context = context || window.document;
12067 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
12068 return [context.createElement(parsed[1])];
12071 if ((parsed = jqLiteBuildFragment(html, context))) {
12072 return parsed.childNodes;
12078 function jqLiteWrapNode(node, wrapper) {
12079 var parent = node.parentNode;
12082 parent.replaceChild(wrapper, node);
12085 wrapper.appendChild(node);
12089 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
12090 var jqLiteContains = window.Node.prototype.contains || function(arg) {
12091 // jshint bitwise: false
12092 return !!(this.compareDocumentPosition(arg) & 16);
12093 // jshint bitwise: true
12096 /////////////////////////////////////////////
12097 function JQLite(element) {
12098 if (element instanceof JQLite) {
12104 if (isString(element)) {
12105 element = trim(element);
12106 argIsString = true;
12108 if (!(this instanceof JQLite)) {
12109 if (argIsString && element.charAt(0) != '<') {
12110 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
12112 return new JQLite(element);
12116 jqLiteAddNodes(this, jqLiteParseHTML(element));
12118 jqLiteAddNodes(this, element);
12122 function jqLiteClone(element) {
12123 return element.cloneNode(true);
12126 function jqLiteDealoc(element, onlyDescendants) {
12127 if (!onlyDescendants) jqLiteRemoveData(element);
12129 if (element.querySelectorAll) {
12130 var descendants = element.querySelectorAll('*');
12131 for (var i = 0, l = descendants.length; i < l; i++) {
12132 jqLiteRemoveData(descendants[i]);
12137 function jqLiteOff(element, type, fn, unsupported) {
12138 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
12140 var expandoStore = jqLiteExpandoStore(element);
12141 var events = expandoStore && expandoStore.events;
12142 var handle = expandoStore && expandoStore.handle;
12144 if (!handle) return; //no listeners registered
12147 for (type in events) {
12148 if (type !== '$destroy') {
12149 removeEventListenerFn(element, type, handle);
12151 delete events[type];
12155 var removeHandler = function(type) {
12156 var listenerFns = events[type];
12157 if (isDefined(fn)) {
12158 arrayRemove(listenerFns || [], fn);
12160 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
12161 removeEventListenerFn(element, type, handle);
12162 delete events[type];
12166 forEach(type.split(' '), function(type) {
12167 removeHandler(type);
12168 if (MOUSE_EVENT_MAP[type]) {
12169 removeHandler(MOUSE_EVENT_MAP[type]);
12175 function jqLiteRemoveData(element, name) {
12176 var expandoId = element.ng339;
12177 var expandoStore = expandoId && jqCache[expandoId];
12179 if (expandoStore) {
12181 delete expandoStore.data[name];
12185 if (expandoStore.handle) {
12186 if (expandoStore.events.$destroy) {
12187 expandoStore.handle({}, '$destroy');
12189 jqLiteOff(element);
12191 delete jqCache[expandoId];
12192 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
12197 function jqLiteExpandoStore(element, createIfNecessary) {
12198 var expandoId = element.ng339,
12199 expandoStore = expandoId && jqCache[expandoId];
12201 if (createIfNecessary && !expandoStore) {
12202 element.ng339 = expandoId = jqNextId();
12203 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
12206 return expandoStore;
12210 function jqLiteData(element, key, value) {
12211 if (jqLiteAcceptsData(element)) {
12213 var isSimpleSetter = isDefined(value);
12214 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
12215 var massGetter = !key;
12216 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
12217 var data = expandoStore && expandoStore.data;
12219 if (isSimpleSetter) { // data('key', value)
12222 if (massGetter) { // data()
12225 if (isSimpleGetter) { // data('key')
12226 // don't force creation of expandoStore if it doesn't exist yet
12227 return data && data[key];
12228 } else { // mass-setter: data({key1: val1, key2: val2})
12236 function jqLiteHasClass(element, selector) {
12237 if (!element.getAttribute) return false;
12238 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
12239 indexOf(" " + selector + " ") > -1);
12242 function jqLiteRemoveClass(element, cssClasses) {
12243 if (cssClasses && element.setAttribute) {
12244 forEach(cssClasses.split(' '), function(cssClass) {
12245 element.setAttribute('class', trim(
12246 (" " + (element.getAttribute('class') || '') + " ")
12247 .replace(/[\n\t]/g, " ")
12248 .replace(" " + trim(cssClass) + " ", " "))
12254 function jqLiteAddClass(element, cssClasses) {
12255 if (cssClasses && element.setAttribute) {
12256 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
12257 .replace(/[\n\t]/g, " ");
12259 forEach(cssClasses.split(' '), function(cssClass) {
12260 cssClass = trim(cssClass);
12261 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
12262 existingClasses += cssClass + ' ';
12266 element.setAttribute('class', trim(existingClasses));
12271 function jqLiteAddNodes(root, elements) {
12272 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
12276 // if a Node (the most common case)
12277 if (elements.nodeType) {
12278 root[root.length++] = elements;
12280 var length = elements.length;
12282 // if an Array or NodeList and not a Window
12283 if (typeof length === 'number' && elements.window !== elements) {
12285 for (var i = 0; i < length; i++) {
12286 root[root.length++] = elements[i];
12290 root[root.length++] = elements;
12297 function jqLiteController(element, name) {
12298 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
12301 function jqLiteInheritedData(element, name, value) {
12302 // if element is the document object work with the html element instead
12303 // this makes $(document).scope() possible
12304 if (element.nodeType == NODE_TYPE_DOCUMENT) {
12305 element = element.documentElement;
12307 var names = isArray(name) ? name : [name];
12310 for (var i = 0, ii = names.length; i < ii; i++) {
12311 if (isDefined(value = jqLite.data(element, names[i]))) return value;
12314 // If dealing with a document fragment node with a host element, and no parent, use the host
12315 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
12316 // to lookup parent controllers.
12317 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
12321 function jqLiteEmpty(element) {
12322 jqLiteDealoc(element, true);
12323 while (element.firstChild) {
12324 element.removeChild(element.firstChild);
12328 function jqLiteRemove(element, keepData) {
12329 if (!keepData) jqLiteDealoc(element);
12330 var parent = element.parentNode;
12331 if (parent) parent.removeChild(element);
12335 function jqLiteDocumentLoaded(action, win) {
12336 win = win || window;
12337 if (win.document.readyState === 'complete') {
12338 // Force the action to be run async for consistent behavior
12339 // from the action's point of view
12340 // i.e. it will definitely not be in a $apply
12341 win.setTimeout(action);
12343 // No need to unbind this handler as load is only ever called once
12344 jqLite(win).on('load', action);
12348 //////////////////////////////////////////
12349 // Functions which are declared directly.
12350 //////////////////////////////////////////
12351 var JQLitePrototype = JQLite.prototype = {
12352 ready: function(fn) {
12355 function trigger() {
12361 // check if document is already loaded
12362 if (window.document.readyState === 'complete') {
12363 window.setTimeout(trigger);
12365 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
12366 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
12368 JQLite(window).on('load', trigger); // fallback to window.onload for others
12372 toString: function() {
12374 forEach(this, function(e) { value.push('' + e);});
12375 return '[' + value.join(', ') + ']';
12378 eq: function(index) {
12379 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
12388 //////////////////////////////////////////
12389 // Functions iterating getter/setters.
12390 // these functions return self on setter and
12392 //////////////////////////////////////////
12393 var BOOLEAN_ATTR = {};
12394 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
12395 BOOLEAN_ATTR[lowercase(value)] = value;
12397 var BOOLEAN_ELEMENTS = {};
12398 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
12399 BOOLEAN_ELEMENTS[value] = true;
12401 var ALIASED_ATTR = {
12402 'ngMinlength': 'minlength',
12403 'ngMaxlength': 'maxlength',
12406 'ngPattern': 'pattern'
12409 function getBooleanAttrName(element, name) {
12410 // check dom last since we will most likely fail on name
12411 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
12413 // booleanAttr is here twice to minimize DOM access
12414 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
12417 function getAliasedAttrName(name) {
12418 return ALIASED_ATTR[name];
12423 removeData: jqLiteRemoveData,
12424 hasData: jqLiteHasData,
12425 cleanData: jqLiteCleanData
12426 }, function(fn, name) {
12432 inheritedData: jqLiteInheritedData,
12434 scope: function(element) {
12435 // Can't use jqLiteData here directly so we stay compatible with jQuery!
12436 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
12439 isolateScope: function(element) {
12440 // Can't use jqLiteData here directly so we stay compatible with jQuery!
12441 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
12444 controller: jqLiteController,
12446 injector: function(element) {
12447 return jqLiteInheritedData(element, '$injector');
12450 removeAttr: function(element, name) {
12451 element.removeAttribute(name);
12454 hasClass: jqLiteHasClass,
12456 css: function(element, name, value) {
12457 name = camelCase(name);
12459 if (isDefined(value)) {
12460 element.style[name] = value;
12462 return element.style[name];
12466 attr: function(element, name, value) {
12467 var nodeType = element.nodeType;
12468 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
12471 var lowercasedName = lowercase(name);
12472 if (BOOLEAN_ATTR[lowercasedName]) {
12473 if (isDefined(value)) {
12475 element[name] = true;
12476 element.setAttribute(name, lowercasedName);
12478 element[name] = false;
12479 element.removeAttribute(lowercasedName);
12482 return (element[name] ||
12483 (element.attributes.getNamedItem(name) || noop).specified)
12487 } else if (isDefined(value)) {
12488 element.setAttribute(name, value);
12489 } else if (element.getAttribute) {
12490 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
12491 // some elements (e.g. Document) don't have get attribute, so return undefined
12492 var ret = element.getAttribute(name, 2);
12493 // normalize non-existing attributes to undefined (as jQuery)
12494 return ret === null ? undefined : ret;
12498 prop: function(element, name, value) {
12499 if (isDefined(value)) {
12500 element[name] = value;
12502 return element[name];
12506 text: (function() {
12510 function getText(element, value) {
12511 if (isUndefined(value)) {
12512 var nodeType = element.nodeType;
12513 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
12515 element.textContent = value;
12519 val: function(element, value) {
12520 if (isUndefined(value)) {
12521 if (element.multiple && nodeName_(element) === 'select') {
12523 forEach(element.options, function(option) {
12524 if (option.selected) {
12525 result.push(option.value || option.text);
12528 return result.length === 0 ? null : result;
12530 return element.value;
12532 element.value = value;
12535 html: function(element, value) {
12536 if (isUndefined(value)) {
12537 return element.innerHTML;
12539 jqLiteDealoc(element, true);
12540 element.innerHTML = value;
12544 }, function(fn, name) {
12546 * Properties: writes return selection, reads return first value
12548 JQLite.prototype[name] = function(arg1, arg2) {
12550 var nodeCount = this.length;
12552 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
12553 // in a way that survives minification.
12554 // jqLiteEmpty takes no arguments but is a setter.
12555 if (fn !== jqLiteEmpty &&
12556 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
12557 if (isObject(arg1)) {
12559 // we are a write, but the object properties are the key/values
12560 for (i = 0; i < nodeCount; i++) {
12561 if (fn === jqLiteData) {
12562 // data() takes the whole object in jQuery
12565 for (key in arg1) {
12566 fn(this[i], key, arg1[key]);
12570 // return self for chaining
12573 // we are a read, so read the first child.
12574 // TODO: do we still need this?
12575 var value = fn.$dv;
12576 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
12577 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
12578 for (var j = 0; j < jj; j++) {
12579 var nodeValue = fn(this[j], arg1, arg2);
12580 value = value ? value + nodeValue : nodeValue;
12585 // we are a write, so apply to all children
12586 for (i = 0; i < nodeCount; i++) {
12587 fn(this[i], arg1, arg2);
12589 // return self for chaining
12595 function createEventHandler(element, events) {
12596 var eventHandler = function(event, type) {
12597 // jQuery specific api
12598 event.isDefaultPrevented = function() {
12599 return event.defaultPrevented;
12602 var eventFns = events[type || event.type];
12603 var eventFnsLength = eventFns ? eventFns.length : 0;
12605 if (!eventFnsLength) return;
12607 if (isUndefined(event.immediatePropagationStopped)) {
12608 var originalStopImmediatePropagation = event.stopImmediatePropagation;
12609 event.stopImmediatePropagation = function() {
12610 event.immediatePropagationStopped = true;
12612 if (event.stopPropagation) {
12613 event.stopPropagation();
12616 if (originalStopImmediatePropagation) {
12617 originalStopImmediatePropagation.call(event);
12622 event.isImmediatePropagationStopped = function() {
12623 return event.immediatePropagationStopped === true;
12626 // Some events have special handlers that wrap the real handler
12627 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
12629 // Copy event handlers in case event handlers array is modified during execution.
12630 if ((eventFnsLength > 1)) {
12631 eventFns = shallowCopy(eventFns);
12634 for (var i = 0; i < eventFnsLength; i++) {
12635 if (!event.isImmediatePropagationStopped()) {
12636 handlerWrapper(element, event, eventFns[i]);
12641 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
12642 // events on `element`
12643 eventHandler.elem = element;
12644 return eventHandler;
12647 function defaultHandlerWrapper(element, event, handler) {
12648 handler.call(element, event);
12651 function specialMouseHandlerWrapper(target, event, handler) {
12652 // Refer to jQuery's implementation of mouseenter & mouseleave
12653 // Read about mouseenter and mouseleave:
12654 // http://www.quirksmode.org/js/events_mouse.html#link8
12655 var related = event.relatedTarget;
12656 // For mousenter/leave call the handler if related is outside the target.
12657 // NB: No relatedTarget if the mouse left/entered the browser window
12658 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
12659 handler.call(target, event);
12663 //////////////////////////////////////////
12664 // Functions iterating traversal.
12665 // These functions chain results into a single
12667 //////////////////////////////////////////
12669 removeData: jqLiteRemoveData,
12671 on: function jqLiteOn(element, type, fn, unsupported) {
12672 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
12674 // Do not add event handlers to non-elements because they will not be cleaned up.
12675 if (!jqLiteAcceptsData(element)) {
12679 var expandoStore = jqLiteExpandoStore(element, true);
12680 var events = expandoStore.events;
12681 var handle = expandoStore.handle;
12684 handle = expandoStore.handle = createEventHandler(element, events);
12687 // http://jsperf.com/string-indexof-vs-split
12688 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
12689 var i = types.length;
12691 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
12692 var eventFns = events[type];
12695 eventFns = events[type] = [];
12696 eventFns.specialHandlerWrapper = specialHandlerWrapper;
12697 if (type !== '$destroy' && !noEventListener) {
12698 addEventListenerFn(element, type, handle);
12707 if (MOUSE_EVENT_MAP[type]) {
12708 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
12709 addHandler(type, undefined, true);
12718 one: function(element, type, fn) {
12719 element = jqLite(element);
12721 //add the listener twice so that when it is called
12722 //you can remove the original function and still be
12723 //able to call element.off(ev, fn) normally
12724 element.on(type, function onFn() {
12725 element.off(type, fn);
12726 element.off(type, onFn);
12728 element.on(type, fn);
12731 replaceWith: function(element, replaceNode) {
12732 var index, parent = element.parentNode;
12733 jqLiteDealoc(element);
12734 forEach(new JQLite(replaceNode), function(node) {
12736 parent.insertBefore(node, index.nextSibling);
12738 parent.replaceChild(node, element);
12744 children: function(element) {
12746 forEach(element.childNodes, function(element) {
12747 if (element.nodeType === NODE_TYPE_ELEMENT) {
12748 children.push(element);
12754 contents: function(element) {
12755 return element.contentDocument || element.childNodes || [];
12758 append: function(element, node) {
12759 var nodeType = element.nodeType;
12760 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
12762 node = new JQLite(node);
12764 for (var i = 0, ii = node.length; i < ii; i++) {
12765 var child = node[i];
12766 element.appendChild(child);
12770 prepend: function(element, node) {
12771 if (element.nodeType === NODE_TYPE_ELEMENT) {
12772 var index = element.firstChild;
12773 forEach(new JQLite(node), function(child) {
12774 element.insertBefore(child, index);
12779 wrap: function(element, wrapNode) {
12780 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
12783 remove: jqLiteRemove,
12785 detach: function(element) {
12786 jqLiteRemove(element, true);
12789 after: function(element, newElement) {
12790 var index = element, parent = element.parentNode;
12791 newElement = new JQLite(newElement);
12793 for (var i = 0, ii = newElement.length; i < ii; i++) {
12794 var node = newElement[i];
12795 parent.insertBefore(node, index.nextSibling);
12800 addClass: jqLiteAddClass,
12801 removeClass: jqLiteRemoveClass,
12803 toggleClass: function(element, selector, condition) {
12805 forEach(selector.split(' '), function(className) {
12806 var classCondition = condition;
12807 if (isUndefined(classCondition)) {
12808 classCondition = !jqLiteHasClass(element, className);
12810 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
12815 parent: function(element) {
12816 var parent = element.parentNode;
12817 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
12820 next: function(element) {
12821 return element.nextElementSibling;
12824 find: function(element, selector) {
12825 if (element.getElementsByTagName) {
12826 return element.getElementsByTagName(selector);
12832 clone: jqLiteClone,
12834 triggerHandler: function(element, event, extraParameters) {
12836 var dummyEvent, eventFnsCopy, handlerArgs;
12837 var eventName = event.type || event;
12838 var expandoStore = jqLiteExpandoStore(element);
12839 var events = expandoStore && expandoStore.events;
12840 var eventFns = events && events[eventName];
12843 // Create a dummy event to pass to the handlers
12845 preventDefault: function() { this.defaultPrevented = true; },
12846 isDefaultPrevented: function() { return this.defaultPrevented === true; },
12847 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
12848 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
12849 stopPropagation: noop,
12854 // If a custom event was provided then extend our dummy event with it
12856 dummyEvent = extend(dummyEvent, event);
12859 // Copy event handlers in case event handlers array is modified during execution.
12860 eventFnsCopy = shallowCopy(eventFns);
12861 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
12863 forEach(eventFnsCopy, function(fn) {
12864 if (!dummyEvent.isImmediatePropagationStopped()) {
12865 fn.apply(element, handlerArgs);
12870 }, function(fn, name) {
12872 * chaining functions
12874 JQLite.prototype[name] = function(arg1, arg2, arg3) {
12877 for (var i = 0, ii = this.length; i < ii; i++) {
12878 if (isUndefined(value)) {
12879 value = fn(this[i], arg1, arg2, arg3);
12880 if (isDefined(value)) {
12881 // any function which returns a value needs to be wrapped
12882 value = jqLite(value);
12885 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
12888 return isDefined(value) ? value : this;
12891 // bind legacy bind/unbind to on/off
12892 JQLite.prototype.bind = JQLite.prototype.on;
12893 JQLite.prototype.unbind = JQLite.prototype.off;
12897 // Provider for private $$jqLite service
12898 function $$jqLiteProvider() {
12899 this.$get = function $$jqLite() {
12900 return extend(JQLite, {
12901 hasClass: function(node, classes) {
12902 if (node.attr) node = node[0];
12903 return jqLiteHasClass(node, classes);
12905 addClass: function(node, classes) {
12906 if (node.attr) node = node[0];
12907 return jqLiteAddClass(node, classes);
12909 removeClass: function(node, classes) {
12910 if (node.attr) node = node[0];
12911 return jqLiteRemoveClass(node, classes);
12918 * Computes a hash of an 'obj'.
12921 * number is number as string
12922 * object is either result of calling $$hashKey function on the object or uniquely generated id,
12923 * that is also assigned to the $$hashKey property of the object.
12926 * @returns {string} hash string such that the same input will have the same hash string.
12927 * The resulting string key is in 'type:hashKey' format.
12929 function hashKey(obj, nextUidFn) {
12930 var key = obj && obj.$$hashKey;
12933 if (typeof key === 'function') {
12934 key = obj.$$hashKey();
12939 var objType = typeof obj;
12940 if (objType == 'function' || (objType == 'object' && obj !== null)) {
12941 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
12943 key = objType + ':' + obj;
12950 * HashMap which can use objects as keys
12952 function HashMap(array, isolatedUid) {
12955 this.nextUid = function() {
12959 forEach(array, this.put, this);
12961 HashMap.prototype = {
12963 * Store key value pair
12964 * @param key key to store can be any type
12965 * @param value value to store can be any type
12967 put: function(key, value) {
12968 this[hashKey(key, this.nextUid)] = value;
12973 * @returns {Object} the value for the key
12975 get: function(key) {
12976 return this[hashKey(key, this.nextUid)];
12980 * Remove the key/value pair
12983 remove: function(key) {
12984 var value = this[key = hashKey(key, this.nextUid)];
12990 var $$HashMapProvider = [function() {
12991 this.$get = [function() {
12999 * @name angular.injector
13003 * Creates an injector object that can be used for retrieving services as well as for
13004 * dependency injection (see {@link guide/di dependency injection}).
13006 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
13007 * {@link angular.module}. The `ng` module must be explicitly added.
13008 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
13009 * disallows argument name annotation inference.
13010 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
13015 * // create an injector
13016 * var $injector = angular.injector(['ng']);
13018 * // use the injector to kick off your application
13019 * // use the type inference to auto inject arguments, or use implicit injection
13020 * $injector.invoke(function($rootScope, $compile, $document) {
13021 * $compile($document)($rootScope);
13022 * $rootScope.$digest();
13026 * Sometimes you want to get access to the injector of a currently running Angular app
13027 * from outside Angular. Perhaps, you want to inject and compile some markup after the
13028 * application has been bootstrapped. You can do this using the extra `injector()` added
13029 * to JQuery/jqLite elements. See {@link angular.element}.
13031 * *This is fairly rare but could be the case if a third party library is injecting the
13034 * In the following example a new block of HTML containing a `ng-controller`
13035 * directive is added to the end of the document body by JQuery. We then compile and link
13036 * it into the current AngularJS scope.
13039 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
13040 * $(document.body).append($div);
13042 * angular.element(document).injector().invoke(function($compile) {
13043 * var scope = angular.element($div).scope();
13044 * $compile($div)(scope);
13056 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
13059 var ARROW_ARG = /^([^\(]+?)=>/;
13060 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
13061 var FN_ARG_SPLIT = /,/;
13062 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
13063 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
13064 var $injectorMinErr = minErr('$injector');
13066 function extractArgs(fn) {
13067 var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
13068 args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
13072 function anonFn(fn) {
13073 // For anonymous functions, showing at the very least the function signature can help in
13075 var args = extractArgs(fn);
13077 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
13082 function annotate(fn, strictDi, name) {
13087 if (typeof fn === 'function') {
13088 if (!($inject = fn.$inject)) {
13092 if (!isString(name) || !name) {
13093 name = fn.name || anonFn(fn);
13095 throw $injectorMinErr('strictdi',
13096 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
13098 argDecl = extractArgs(fn);
13099 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
13100 arg.replace(FN_ARG, function(all, underscore, name) {
13101 $inject.push(name);
13105 fn.$inject = $inject;
13107 } else if (isArray(fn)) {
13108 last = fn.length - 1;
13109 assertArgFn(fn[last], 'fn');
13110 $inject = fn.slice(0, last);
13112 assertArgFn(fn, 'fn', true);
13117 ///////////////////////////////////////
13125 * `$injector` is used to retrieve object instances as defined by
13126 * {@link auto.$provide provider}, instantiate types, invoke methods,
13127 * and load modules.
13129 * The following always holds true:
13132 * var $injector = angular.injector();
13133 * expect($injector.get('$injector')).toBe($injector);
13134 * expect($injector.invoke(function($injector) {
13135 * return $injector;
13136 * })).toBe($injector);
13139 * # Injection Function Annotation
13141 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
13142 * following are all valid ways of annotating function with injection arguments and are equivalent.
13145 * // inferred (only works if code not minified/obfuscated)
13146 * $injector.invoke(function(serviceA){});
13149 * function explicit(serviceA) {};
13150 * explicit.$inject = ['serviceA'];
13151 * $injector.invoke(explicit);
13154 * $injector.invoke(['serviceA', function(serviceA){}]);
13159 * In JavaScript calling `toString()` on a function returns the function definition. The definition
13160 * can then be parsed and the function arguments can be extracted. This method of discovering
13161 * annotations is disallowed when the injector is in strict mode.
13162 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
13165 * ## `$inject` Annotation
13166 * By adding an `$inject` property onto a function the injection parameters can be specified.
13169 * As an array of injection names, where the last item in the array is the function to call.
13174 * @name $injector#get
13177 * Return an instance of the service.
13179 * @param {string} name The name of the instance to retrieve.
13180 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
13181 * @return {*} The instance.
13186 * @name $injector#invoke
13189 * Invoke the method and supply the method arguments from the `$injector`.
13191 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
13192 * injected according to the {@link guide/di $inject Annotation} rules.
13193 * @param {Object=} self The `this` for the invoked method.
13194 * @param {Object=} locals Optional object. If preset then any argument names are read from this
13195 * object first, before the `$injector` is consulted.
13196 * @returns {*} the value returned by the invoked `fn` function.
13201 * @name $injector#has
13204 * Allows the user to query if the particular service exists.
13206 * @param {string} name Name of the service to query.
13207 * @returns {boolean} `true` if injector has given service.
13212 * @name $injector#instantiate
13214 * Create a new instance of JS type. The method takes a constructor function, invokes the new
13215 * operator, and supplies all of the arguments to the constructor function as specified by the
13216 * constructor annotation.
13218 * @param {Function} Type Annotated constructor function.
13219 * @param {Object=} locals Optional object. If preset then any argument names are read from this
13220 * object first, before the `$injector` is consulted.
13221 * @returns {Object} new instance of `Type`.
13226 * @name $injector#annotate
13229 * Returns an array of service names which the function is requesting for injection. This API is
13230 * used by the injector to determine which services need to be injected into the function when the
13231 * function is invoked. There are three ways in which the function can be annotated with the needed
13236 * The simplest form is to extract the dependencies from the arguments of the function. This is done
13237 * by converting the function into a string using `toString()` method and extracting the argument
13241 * function MyController($scope, $route) {
13246 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
13249 * You can disallow this method by using strict injection mode.
13251 * This method does not work with code minification / obfuscation. For this reason the following
13252 * annotation strategies are supported.
13254 * # The `$inject` property
13256 * If a function has an `$inject` property and its value is an array of strings, then the strings
13257 * represent names of services to be injected into the function.
13260 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
13263 * // Define function dependencies
13264 * MyController['$inject'] = ['$scope', '$route'];
13267 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
13270 * # The array notation
13272 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
13273 * is very inconvenient. In these situations using the array notation to specify the dependencies in
13274 * a way that survives minification is a better choice:
13277 * // We wish to write this (not minification / obfuscation safe)
13278 * injector.invoke(function($compile, $rootScope) {
13282 * // We are forced to write break inlining
13283 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
13286 * tmpFn.$inject = ['$compile', '$rootScope'];
13287 * injector.invoke(tmpFn);
13289 * // To better support inline function the inline annotation is supported
13290 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
13295 * expect(injector.annotate(
13296 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
13297 * ).toEqual(['$compile', '$rootScope']);
13300 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
13301 * be retrieved as described above.
13303 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
13305 * @returns {Array.<string>} The names of the services which the function requires.
13317 * The {@link auto.$provide $provide} service has a number of methods for registering components
13318 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
13319 * {@link angular.Module}.
13321 * An Angular **service** is a singleton object created by a **service factory**. These **service
13322 * factories** are functions which, in turn, are created by a **service provider**.
13323 * The **service providers** are constructor functions. When instantiated they must contain a
13324 * property called `$get`, which holds the **service factory** function.
13326 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
13327 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
13328 * function to get the instance of the **service**.
13330 * Often services have no configuration options and there is no need to add methods to the service
13331 * provider. The provider will be no more than a constructor function with a `$get` property. For
13332 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
13333 * services without specifying a provider.
13335 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
13336 * {@link auto.$injector $injector}
13337 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
13338 * providers and services.
13339 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
13340 * services, not providers.
13341 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
13342 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
13343 * given factory function.
13344 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
13345 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
13346 * a new object using the given constructor function.
13348 * See the individual methods for more information and examples.
13353 * @name $provide#provider
13356 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
13357 * are constructor functions, whose instances are responsible for "providing" a factory for a
13360 * Service provider names start with the name of the service they provide followed by `Provider`.
13361 * For example, the {@link ng.$log $log} service has a provider called
13362 * {@link ng.$logProvider $logProvider}.
13364 * Service provider objects can have additional methods which allow configuration of the provider
13365 * and its service. Importantly, you can configure what kind of service is created by the `$get`
13366 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
13367 * method {@link ng.$logProvider#debugEnabled debugEnabled}
13368 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
13371 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
13373 * @param {(Object|function())} provider If the provider is:
13375 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
13376 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
13377 * - `Constructor`: a new instance of the provider will be created using
13378 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
13380 * @returns {Object} registered provider instance
13384 * The following example shows how to create a simple event tracking service and register it using
13385 * {@link auto.$provide#provider $provide.provider()}.
13388 * // Define the eventTracker provider
13389 * function EventTrackerProvider() {
13390 * var trackingUrl = '/track';
13392 * // A provider method for configuring where the tracked events should been saved
13393 * this.setTrackingUrl = function(url) {
13394 * trackingUrl = url;
13397 * // The service factory function
13398 * this.$get = ['$http', function($http) {
13399 * var trackedEvents = {};
13401 * // Call this to track an event
13402 * event: function(event) {
13403 * var count = trackedEvents[event] || 0;
13405 * trackedEvents[event] = count;
13408 * // Call this to save the tracked events to the trackingUrl
13409 * save: function() {
13410 * $http.post(trackingUrl, trackedEvents);
13416 * describe('eventTracker', function() {
13419 * beforeEach(module(function($provide) {
13420 * // Register the eventTracker provider
13421 * $provide.provider('eventTracker', EventTrackerProvider);
13424 * beforeEach(module(function(eventTrackerProvider) {
13425 * // Configure eventTracker provider
13426 * eventTrackerProvider.setTrackingUrl('/custom-track');
13429 * it('tracks events', inject(function(eventTracker) {
13430 * expect(eventTracker.event('login')).toEqual(1);
13431 * expect(eventTracker.event('login')).toEqual(2);
13434 * it('saves to the tracking url', inject(function(eventTracker, $http) {
13435 * postSpy = spyOn($http, 'post');
13436 * eventTracker.event('login');
13437 * eventTracker.save();
13438 * expect(postSpy).toHaveBeenCalled();
13439 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
13440 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
13441 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
13449 * @name $provide#factory
13452 * Register a **service factory**, which will be called to return the service instance.
13453 * This is short for registering a service where its provider consists of only a `$get` property,
13454 * which is the given service factory function.
13455 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
13456 * configure your service in a provider.
13458 * @param {string} name The name of the instance.
13459 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
13460 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
13461 * @returns {Object} registered provider instance
13464 * Here is an example of registering a service
13466 * $provide.factory('ping', ['$http', function($http) {
13467 * return function ping() {
13468 * return $http.send('/ping');
13472 * You would then inject and use this service like this:
13474 * someModule.controller('Ctrl', ['ping', function(ping) {
13483 * @name $provide#service
13486 * Register a **service constructor**, which will be invoked with `new` to create the service
13488 * This is short for registering a service where its provider's `$get` property is a factory
13489 * function that returns an instance instantiated by the injector from the service constructor
13492 * Internally it looks a bit like this:
13496 * $get: function() {
13497 * return $injector.instantiate(constructor);
13503 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
13506 * @param {string} name The name of the instance.
13507 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
13508 * that will be instantiated.
13509 * @returns {Object} registered provider instance
13512 * Here is an example of registering a service using
13513 * {@link auto.$provide#service $provide.service(class)}.
13515 * var Ping = function($http) {
13516 * this.$http = $http;
13519 * Ping.$inject = ['$http'];
13521 * Ping.prototype.send = function() {
13522 * return this.$http.get('/ping');
13524 * $provide.service('ping', Ping);
13526 * You would then inject and use this service like this:
13528 * someModule.controller('Ctrl', ['ping', function(ping) {
13537 * @name $provide#value
13540 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
13541 * number, an array, an object or a function. This is short for registering a service where its
13542 * provider's `$get` property is a factory function that takes no arguments and returns the **value
13543 * service**. That also means it is not possible to inject other services into a value service.
13545 * Value services are similar to constant services, except that they cannot be injected into a
13546 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
13547 * an Angular {@link auto.$provide#decorator decorator}.
13549 * @param {string} name The name of the instance.
13550 * @param {*} value The value.
13551 * @returns {Object} registered provider instance
13554 * Here are some examples of creating value services.
13556 * $provide.value('ADMIN_USER', 'admin');
13558 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
13560 * $provide.value('halfOf', function(value) {
13561 * return value / 2;
13569 * @name $provide#constant
13572 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
13573 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
13574 * possible to inject other services into a constant.
13576 * But unlike {@link auto.$provide#value value}, a constant can be
13577 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
13578 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
13580 * @param {string} name The name of the constant.
13581 * @param {*} value The constant value.
13582 * @returns {Object} registered instance
13585 * Here a some examples of creating constants:
13587 * $provide.constant('SHARD_HEIGHT', 306);
13589 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
13591 * $provide.constant('double', function(value) {
13592 * return value * 2;
13600 * @name $provide#decorator
13603 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
13604 * intercepts the creation of a service, allowing it to override or modify the behavior of the
13605 * service. The object returned by the decorator may be the original service, or a new service
13606 * object which replaces or wraps and delegates to the original service.
13608 * @param {string} name The name of the service to decorate.
13609 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
13610 * instantiated and should return the decorated service instance. The function is called using
13611 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
13612 * Local injection arguments:
13614 * * `$delegate` - The original service instance, which can be monkey patched, configured,
13615 * decorated or delegated to.
13618 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
13619 * calls to {@link ng.$log#error $log.warn()}.
13621 * $provide.decorator('$log', ['$delegate', function($delegate) {
13622 * $delegate.warn = $delegate.error;
13623 * return $delegate;
13629 function createInjector(modulesToLoad, strictDi) {
13630 strictDi = (strictDi === true);
13631 var INSTANTIATING = {},
13632 providerSuffix = 'Provider',
13634 loadedModules = new HashMap([], true),
13637 provider: supportObject(provider),
13638 factory: supportObject(factory),
13639 service: supportObject(service),
13640 value: supportObject(value),
13641 constant: supportObject(constant),
13642 decorator: decorator
13645 providerInjector = (providerCache.$injector =
13646 createInternalInjector(providerCache, function(serviceName, caller) {
13647 if (angular.isString(caller)) {
13650 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
13652 instanceCache = {},
13653 protoInstanceInjector =
13654 createInternalInjector(instanceCache, function(serviceName, caller) {
13655 var provider = providerInjector.get(serviceName + providerSuffix, caller);
13656 return instanceInjector.invoke(
13657 provider.$get, provider, undefined, serviceName);
13659 instanceInjector = protoInstanceInjector;
13661 providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
13662 var runBlocks = loadModules(modulesToLoad);
13663 instanceInjector = protoInstanceInjector.get('$injector');
13664 instanceInjector.strictDi = strictDi;
13665 forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
13667 return instanceInjector;
13669 ////////////////////////////////////
13671 ////////////////////////////////////
13673 function supportObject(delegate) {
13674 return function(key, value) {
13675 if (isObject(key)) {
13676 forEach(key, reverseParams(delegate));
13678 return delegate(key, value);
13683 function provider(name, provider_) {
13684 assertNotHasOwnProperty(name, 'service');
13685 if (isFunction(provider_) || isArray(provider_)) {
13686 provider_ = providerInjector.instantiate(provider_);
13688 if (!provider_.$get) {
13689 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
13691 return providerCache[name + providerSuffix] = provider_;
13694 function enforceReturnValue(name, factory) {
13695 return function enforcedReturnValue() {
13696 var result = instanceInjector.invoke(factory, this);
13697 if (isUndefined(result)) {
13698 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
13704 function factory(name, factoryFn, enforce) {
13705 return provider(name, {
13706 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
13710 function service(name, constructor) {
13711 return factory(name, ['$injector', function($injector) {
13712 return $injector.instantiate(constructor);
13716 function value(name, val) { return factory(name, valueFn(val), false); }
13718 function constant(name, value) {
13719 assertNotHasOwnProperty(name, 'constant');
13720 providerCache[name] = value;
13721 instanceCache[name] = value;
13724 function decorator(serviceName, decorFn) {
13725 var origProvider = providerInjector.get(serviceName + providerSuffix),
13726 orig$get = origProvider.$get;
13728 origProvider.$get = function() {
13729 var origInstance = instanceInjector.invoke(orig$get, origProvider);
13730 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
13734 ////////////////////////////////////
13736 ////////////////////////////////////
13737 function loadModules(modulesToLoad) {
13738 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
13739 var runBlocks = [], moduleFn;
13740 forEach(modulesToLoad, function(module) {
13741 if (loadedModules.get(module)) return;
13742 loadedModules.put(module, true);
13744 function runInvokeQueue(queue) {
13746 for (i = 0, ii = queue.length; i < ii; i++) {
13747 var invokeArgs = queue[i],
13748 provider = providerInjector.get(invokeArgs[0]);
13750 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
13755 if (isString(module)) {
13756 moduleFn = angularModule(module);
13757 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
13758 runInvokeQueue(moduleFn._invokeQueue);
13759 runInvokeQueue(moduleFn._configBlocks);
13760 } else if (isFunction(module)) {
13761 runBlocks.push(providerInjector.invoke(module));
13762 } else if (isArray(module)) {
13763 runBlocks.push(providerInjector.invoke(module));
13765 assertArgFn(module, 'module');
13768 if (isArray(module)) {
13769 module = module[module.length - 1];
13771 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
13772 // Safari & FF's stack traces don't contain error.message content
13773 // unlike those of Chrome and IE
13774 // So if stack doesn't contain message, we create a new string that contains both.
13775 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
13777 e = e.message + '\n' + e.stack;
13779 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
13780 module, e.stack || e.message || e);
13786 ////////////////////////////////////
13787 // internal Injector
13788 ////////////////////////////////////
13790 function createInternalInjector(cache, factory) {
13792 function getService(serviceName, caller) {
13793 if (cache.hasOwnProperty(serviceName)) {
13794 if (cache[serviceName] === INSTANTIATING) {
13795 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
13796 serviceName + ' <- ' + path.join(' <- '));
13798 return cache[serviceName];
13801 path.unshift(serviceName);
13802 cache[serviceName] = INSTANTIATING;
13803 return cache[serviceName] = factory(serviceName, caller);
13805 if (cache[serviceName] === INSTANTIATING) {
13806 delete cache[serviceName];
13816 function injectionArgs(fn, locals, serviceName) {
13818 $inject = createInjector.$$annotate(fn, strictDi, serviceName);
13820 for (var i = 0, length = $inject.length; i < length; i++) {
13821 var key = $inject[i];
13822 if (typeof key !== 'string') {
13823 throw $injectorMinErr('itkn',
13824 'Incorrect injection token! Expected service name as string, got {0}', key);
13826 args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
13827 getService(key, serviceName));
13832 function isClass(func) {
13833 // IE 9-11 do not support classes and IE9 leaks with the code below.
13837 // Workaround for MS Edge.
13838 // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
13839 return typeof func === 'function'
13840 && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
13843 function invoke(fn, self, locals, serviceName) {
13844 if (typeof locals === 'string') {
13845 serviceName = locals;
13849 var args = injectionArgs(fn, locals, serviceName);
13851 fn = fn[fn.length - 1];
13854 if (!isClass(fn)) {
13855 // http://jsperf.com/angularjs-invoke-apply-vs-switch
13857 return fn.apply(self, args);
13859 args.unshift(null);
13860 return new (Function.prototype.bind.apply(fn, args))();
13865 function instantiate(Type, locals, serviceName) {
13866 // Check if Type is annotated and use just the given function at n-1 as parameter
13867 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
13868 var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
13869 var args = injectionArgs(Type, locals, serviceName);
13870 // Empty object at position 0 is ignored for invocation with `new`, but required.
13871 args.unshift(null);
13872 return new (Function.prototype.bind.apply(ctor, args))();
13878 instantiate: instantiate,
13880 annotate: createInjector.$$annotate,
13881 has: function(name) {
13882 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
13888 createInjector.$$annotate = annotate;
13892 * @name $anchorScrollProvider
13895 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
13896 * {@link ng.$location#hash $location.hash()} changes.
13898 function $AnchorScrollProvider() {
13900 var autoScrollingEnabled = true;
13904 * @name $anchorScrollProvider#disableAutoScrolling
13907 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
13908 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
13909 * Use this method to disable automatic scrolling.
13911 * If automatic scrolling is disabled, one must explicitly call
13912 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
13915 this.disableAutoScrolling = function() {
13916 autoScrollingEnabled = false;
13921 * @name $anchorScroll
13923 * @requires $window
13924 * @requires $location
13925 * @requires $rootScope
13928 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
13929 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
13931 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
13933 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
13934 * match any anchor whenever it changes. This can be disabled by calling
13935 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
13937 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
13938 * vertical scroll-offset (either fixed or dynamic).
13940 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
13941 * {@link ng.$location#hash $location.hash()} will be used.
13943 * @property {(number|function|jqLite)} yOffset
13944 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
13945 * positioned elements at the top of the page, such as navbars, headers etc.
13947 * `yOffset` can be specified in various ways:
13948 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
13949 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
13950 * a number representing the offset (in pixels).<br /><br />
13951 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
13952 * the top of the page to the element's bottom will be used as offset.<br />
13953 * **Note**: The element will be taken into account only as long as its `position` is set to
13954 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
13955 * their height and/or positioning according to the viewport's size.
13958 * <div class="alert alert-warning">
13959 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
13960 * not some child element.
13964 <example module="anchorScrollExample">
13965 <file name="index.html">
13966 <div id="scrollArea" ng-controller="ScrollController">
13967 <a ng-click="gotoBottom()">Go to bottom</a>
13968 <a id="bottom"></a> You're at the bottom!
13971 <file name="script.js">
13972 angular.module('anchorScrollExample', [])
13973 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
13974 function ($scope, $location, $anchorScroll) {
13975 $scope.gotoBottom = function() {
13976 // set the location.hash to the id of
13977 // the element you wish to scroll to.
13978 $location.hash('bottom');
13980 // call $anchorScroll()
13985 <file name="style.css">
13993 margin-top: 2000px;
13999 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
14000 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
14003 <example module="anchorScrollOffsetExample">
14004 <file name="index.html">
14005 <div class="fixed-header" ng-controller="headerCtrl">
14006 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
14010 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
14014 <file name="script.js">
14015 angular.module('anchorScrollOffsetExample', [])
14016 .run(['$anchorScroll', function($anchorScroll) {
14017 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
14019 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
14020 function ($anchorScroll, $location, $scope) {
14021 $scope.gotoAnchor = function(x) {
14022 var newHash = 'anchor' + x;
14023 if ($location.hash() !== newHash) {
14024 // set the $location.hash to `newHash` and
14025 // $anchorScroll will automatically scroll to it
14026 $location.hash('anchor' + x);
14028 // call $anchorScroll() explicitly,
14029 // since $location.hash hasn't changed
14036 <file name="style.css">
14042 border: 2px dashed DarkOrchid;
14043 padding: 10px 10px 200px 10px;
14047 background-color: rgba(0, 0, 0, 0.2);
14050 top: 0; left: 0; right: 0;
14053 .fixed-header > a {
14054 display: inline-block;
14060 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
14061 var document = $window.document;
14063 // Helper function to get first anchor from a NodeList
14064 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
14065 // and working in all supported browsers.)
14066 function getFirstAnchor(list) {
14068 Array.prototype.some.call(list, function(element) {
14069 if (nodeName_(element) === 'a') {
14077 function getYOffset() {
14079 var offset = scroll.yOffset;
14081 if (isFunction(offset)) {
14083 } else if (isElement(offset)) {
14084 var elem = offset[0];
14085 var style = $window.getComputedStyle(elem);
14086 if (style.position !== 'fixed') {
14089 offset = elem.getBoundingClientRect().bottom;
14091 } else if (!isNumber(offset)) {
14098 function scrollTo(elem) {
14100 elem.scrollIntoView();
14102 var offset = getYOffset();
14105 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
14106 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
14107 // top of the viewport.
14109 // IF the number of pixels from the top of `elem` to the end of the page's content is less
14110 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
14111 // way down the page.
14113 // This is often the case for elements near the bottom of the page.
14115 // In such cases we do not need to scroll the whole `offset` up, just the difference between
14116 // the top of the element and the offset, which is enough to align the top of `elem` at the
14117 // desired position.
14118 var elemTop = elem.getBoundingClientRect().top;
14119 $window.scrollBy(0, elemTop - offset);
14122 $window.scrollTo(0, 0);
14126 function scroll(hash) {
14127 hash = isString(hash) ? hash : $location.hash();
14130 // empty hash, scroll to the top of the page
14131 if (!hash) scrollTo(null);
14133 // element with given id
14134 else if ((elm = document.getElementById(hash))) scrollTo(elm);
14136 // first anchor with given name :-D
14137 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
14139 // no element and hash == 'top', scroll to the top of the page
14140 else if (hash === 'top') scrollTo(null);
14143 // does not scroll when user clicks on anchor link that is currently on
14144 // (no url change, no $location.hash() change), browser native does scroll
14145 if (autoScrollingEnabled) {
14146 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
14147 function autoScrollWatchAction(newVal, oldVal) {
14148 // skip the initial scroll if $location.hash is empty
14149 if (newVal === oldVal && newVal === '') return;
14151 jqLiteDocumentLoaded(function() {
14152 $rootScope.$evalAsync(scroll);
14161 var $animateMinErr = minErr('$animate');
14162 var ELEMENT_NODE = 1;
14163 var NG_ANIMATE_CLASSNAME = 'ng-animate';
14165 function mergeClasses(a,b) {
14166 if (!a && !b) return '';
14169 if (isArray(a)) a = a.join(' ');
14170 if (isArray(b)) b = b.join(' ');
14171 return a + ' ' + b;
14174 function extractElementNode(element) {
14175 for (var i = 0; i < element.length; i++) {
14176 var elm = element[i];
14177 if (elm.nodeType === ELEMENT_NODE) {
14183 function splitClasses(classes) {
14184 if (isString(classes)) {
14185 classes = classes.split(' ');
14188 // Use createMap() to prevent class assumptions involving property names in
14189 // Object.prototype
14190 var obj = createMap();
14191 forEach(classes, function(klass) {
14192 // sometimes the split leaves empty string values
14193 // incase extra spaces were applied to the options
14194 if (klass.length) {
14201 // if any other type of options value besides an Object value is
14202 // passed into the $animate.method() animation then this helper code
14203 // will be run which will ignore it. While this patch is not the
14204 // greatest solution to this, a lot of existing plugins depend on
14205 // $animate to either call the callback (< 1.2) or return a promise
14206 // that can be changed. This helper function ensures that the options
14207 // are wiped clean incase a callback function is provided.
14208 function prepareAnimateOptions(options) {
14209 return isObject(options)
14214 var $$CoreAnimateJsProvider = function() {
14218 // this is prefixed with Core since it conflicts with
14219 // the animateQueueProvider defined in ngAnimate/animateQueue.js
14220 var $$CoreAnimateQueueProvider = function() {
14221 var postDigestQueue = new HashMap();
14222 var postDigestElements = [];
14224 this.$get = ['$$AnimateRunner', '$rootScope',
14225 function($$AnimateRunner, $rootScope) {
14232 push: function(element, event, options, domOperation) {
14233 domOperation && domOperation();
14235 options = options || {};
14236 options.from && element.css(options.from);
14237 options.to && element.css(options.to);
14239 if (options.addClass || options.removeClass) {
14240 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
14243 var runner = new $$AnimateRunner(); // jshint ignore:line
14245 // since there are no animations to run the runner needs to be
14246 // notified that the animation call is complete.
14253 function updateData(data, classes, value) {
14254 var changed = false;
14256 classes = isString(classes) ? classes.split(' ') :
14257 isArray(classes) ? classes : [];
14258 forEach(classes, function(className) {
14261 data[className] = value;
14268 function handleCSSClassChanges() {
14269 forEach(postDigestElements, function(element) {
14270 var data = postDigestQueue.get(element);
14272 var existing = splitClasses(element.attr('class'));
14275 forEach(data, function(status, className) {
14276 var hasClass = !!existing[className];
14277 if (status !== hasClass) {
14279 toAdd += (toAdd.length ? ' ' : '') + className;
14281 toRemove += (toRemove.length ? ' ' : '') + className;
14286 forEach(element, function(elm) {
14287 toAdd && jqLiteAddClass(elm, toAdd);
14288 toRemove && jqLiteRemoveClass(elm, toRemove);
14290 postDigestQueue.remove(element);
14293 postDigestElements.length = 0;
14297 function addRemoveClassesPostDigest(element, add, remove) {
14298 var data = postDigestQueue.get(element) || {};
14300 var classesAdded = updateData(data, add, true);
14301 var classesRemoved = updateData(data, remove, false);
14303 if (classesAdded || classesRemoved) {
14305 postDigestQueue.put(element, data);
14306 postDigestElements.push(element);
14308 if (postDigestElements.length === 1) {
14309 $rootScope.$$postDigest(handleCSSClassChanges);
14318 * @name $animateProvider
14321 * Default implementation of $animate that doesn't perform any animations, instead just
14322 * synchronously performs DOM updates and resolves the returned runner promise.
14324 * In order to enable animations the `ngAnimate` module has to be loaded.
14326 * To see the functional implementation check out `src/ngAnimate/animate.js`.
14328 var $AnimateProvider = ['$provide', function($provide) {
14329 var provider = this;
14331 this.$$registeredAnimations = Object.create(null);
14335 * @name $animateProvider#register
14338 * Registers a new injectable animation factory function. The factory function produces the
14339 * animation object which contains callback functions for each event that is expected to be
14342 * * `eventFn`: `function(element, ... , doneFunction, options)`
14343 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
14344 * on the type of animation additional arguments will be injected into the animation function. The
14345 * list below explains the function signatures for the different animation methods:
14347 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
14348 * - addClass: function(element, addedClasses, doneFunction, options)
14349 * - removeClass: function(element, removedClasses, doneFunction, options)
14350 * - enter, leave, move: function(element, doneFunction, options)
14351 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
14353 * Make sure to trigger the `doneFunction` once the animation is fully complete.
14357 * //enter, leave, move signature
14358 * eventFn : function(element, done, options) {
14359 * //code to run the animation
14360 * //once complete, then run done()
14361 * return function endFunction(wasCancelled) {
14362 * //code to cancel the animation
14368 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
14369 * @param {Function} factory The factory function that will be executed to return the animation
14372 this.register = function(name, factory) {
14373 if (name && name.charAt(0) !== '.') {
14374 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
14377 var key = name + '-animation';
14378 provider.$$registeredAnimations[name.substr(1)] = key;
14379 $provide.factory(key, factory);
14384 * @name $animateProvider#classNameFilter
14387 * Sets and/or returns the CSS class regular expression that is checked when performing
14388 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
14389 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
14390 * When setting the `classNameFilter` value, animations will only be performed on elements
14391 * that successfully match the filter expression. This in turn can boost performance
14392 * for low-powered devices as well as applications containing a lot of structural operations.
14393 * @param {RegExp=} expression The className expression which will be checked against all animations
14394 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
14396 this.classNameFilter = function(expression) {
14397 if (arguments.length === 1) {
14398 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
14399 if (this.$$classNameFilter) {
14400 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
14401 if (reservedRegex.test(this.$$classNameFilter.toString())) {
14402 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
14407 return this.$$classNameFilter;
14410 this.$get = ['$$animateQueue', function($$animateQueue) {
14411 function domInsert(element, parentElement, afterElement) {
14412 // if for some reason the previous element was removed
14413 // from the dom sometime before this code runs then let's
14414 // just stick to using the parent element as the anchor
14415 if (afterElement) {
14416 var afterNode = extractElementNode(afterElement);
14417 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
14418 afterElement = null;
14421 afterElement ? afterElement.after(element) : parentElement.prepend(element);
14427 * @description The $animate service exposes a series of DOM utility methods that provide support
14428 * for animation hooks. The default behavior is the application of DOM operations, however,
14429 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
14430 * to ensure that animation runs with the triggered DOM operation.
14432 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
14433 * included and only when it is active then the animation hooks that `$animate` triggers will be
14434 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
14435 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
14436 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
14438 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
14440 * To learn more about enabling animation support, click here to visit the
14441 * {@link ngAnimate ngAnimate module page}.
14444 // we don't call it directly since non-existant arguments may
14445 // be interpreted as null within the sub enabled function
14450 * @name $animate#on
14452 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
14453 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
14454 * is fired with the following params:
14457 * $animate.on('enter', container,
14458 * function callback(element, phase) {
14459 * // cool we detected an enter animation within the container
14464 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
14465 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
14466 * as well as among its children
14467 * @param {Function} callback the callback function that will be fired when the listener is triggered
14469 * The arguments present in the callback function are:
14470 * * `element` - The captured DOM element that the animation was fired on.
14471 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
14473 on: $$animateQueue.on,
14478 * @name $animate#off
14480 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
14481 * can be used in three different ways depending on the arguments:
14484 * // remove all the animation event listeners listening for `enter`
14485 * $animate.off('enter');
14487 * // remove listeners for all animation events from the container element
14488 * $animate.off(container);
14490 * // remove all the animation event listeners listening for `enter` on the given element and its children
14491 * $animate.off('enter', container);
14493 * // remove the event listener function provided by `callback` that is set
14494 * // to listen for `enter` on the given `container` as well as its children
14495 * $animate.off('enter', container, callback);
14498 * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
14499 * addClass, removeClass, etc...), or the container element. If it is the element, all other
14500 * arguments are ignored.
14501 * @param {DOMElement=} container the container element the event listener was placed on
14502 * @param {Function=} callback the callback function that was registered as the listener
14504 off: $$animateQueue.off,
14508 * @name $animate#pin
14510 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
14511 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
14512 * element despite being outside the realm of the application or within another application. Say for example if the application
14513 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
14514 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
14515 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
14517 * Note that this feature is only active when the `ngAnimate` module is used.
14519 * @param {DOMElement} element the external element that will be pinned
14520 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
14522 pin: $$animateQueue.pin,
14527 * @name $animate#enabled
14529 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
14530 * function can be called in four ways:
14533 * // returns true or false
14534 * $animate.enabled();
14536 * // changes the enabled state for all animations
14537 * $animate.enabled(false);
14538 * $animate.enabled(true);
14540 * // returns true or false if animations are enabled for an element
14541 * $animate.enabled(element);
14543 * // changes the enabled state for an element and its children
14544 * $animate.enabled(element, true);
14545 * $animate.enabled(element, false);
14548 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
14549 * @param {boolean=} enabled whether or not the animations will be enabled for the element
14551 * @return {boolean} whether or not animations are enabled
14553 enabled: $$animateQueue.enabled,
14557 * @name $animate#cancel
14559 * @description Cancels the provided animation.
14561 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
14563 cancel: function(runner) {
14564 runner.end && runner.end();
14570 * @name $animate#enter
14572 * @description Inserts the element into the DOM either after the `after` element (if provided) or
14573 * as the first child within the `parent` element and then triggers an animation.
14574 * A promise is returned that will be resolved during the next digest once the animation
14577 * @param {DOMElement} element the element which will be inserted into the DOM
14578 * @param {DOMElement} parent the parent element which will append the element as
14579 * a child (so long as the after element is not present)
14580 * @param {DOMElement=} after the sibling element after which the element will be appended
14581 * @param {object=} options an optional collection of options/styles that will be applied to the element
14583 * @return {Promise} the animation callback promise
14585 enter: function(element, parent, after, options) {
14586 parent = parent && jqLite(parent);
14587 after = after && jqLite(after);
14588 parent = parent || after.parent();
14589 domInsert(element, parent, after);
14590 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
14596 * @name $animate#move
14598 * @description Inserts (moves) the element into its new position in the DOM either after
14599 * the `after` element (if provided) or as the first child within the `parent` element
14600 * and then triggers an animation. A promise is returned that will be resolved
14601 * during the next digest once the animation has completed.
14603 * @param {DOMElement} element the element which will be moved into the new DOM position
14604 * @param {DOMElement} parent the parent element which will append the element as
14605 * a child (so long as the after element is not present)
14606 * @param {DOMElement=} after the sibling element after which the element will be appended
14607 * @param {object=} options an optional collection of options/styles that will be applied to the element
14609 * @return {Promise} the animation callback promise
14611 move: function(element, parent, after, options) {
14612 parent = parent && jqLite(parent);
14613 after = after && jqLite(after);
14614 parent = parent || after.parent();
14615 domInsert(element, parent, after);
14616 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
14621 * @name $animate#leave
14623 * @description Triggers an animation and then removes the element from the DOM.
14624 * When the function is called a promise is returned that will be resolved during the next
14625 * digest once the animation has completed.
14627 * @param {DOMElement} element the element which will be removed from the DOM
14628 * @param {object=} options an optional collection of options/styles that will be applied to the element
14630 * @return {Promise} the animation callback promise
14632 leave: function(element, options) {
14633 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
14640 * @name $animate#addClass
14643 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
14644 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
14645 * animation if element already contains the CSS class or if the class is removed at a later step.
14646 * Note that class-based animations are treated differently compared to structural animations
14647 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14648 * depending if CSS or JavaScript animations are used.
14650 * @param {DOMElement} element the element which the CSS classes will be applied to
14651 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
14652 * @param {object=} options an optional collection of options/styles that will be applied to the element
14654 * @return {Promise} the animation callback promise
14656 addClass: function(element, className, options) {
14657 options = prepareAnimateOptions(options);
14658 options.addClass = mergeClasses(options.addclass, className);
14659 return $$animateQueue.push(element, 'addClass', options);
14664 * @name $animate#removeClass
14667 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
14668 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
14669 * animation if element does not contain the CSS class or if the class is added at a later step.
14670 * Note that class-based animations are treated differently compared to structural animations
14671 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14672 * depending if CSS or JavaScript animations are used.
14674 * @param {DOMElement} element the element which the CSS classes will be applied to
14675 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
14676 * @param {object=} options an optional collection of options/styles that will be applied to the element
14678 * @return {Promise} the animation callback promise
14680 removeClass: function(element, className, options) {
14681 options = prepareAnimateOptions(options);
14682 options.removeClass = mergeClasses(options.removeClass, className);
14683 return $$animateQueue.push(element, 'removeClass', options);
14688 * @name $animate#setClass
14691 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
14692 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
14693 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
14694 * passed. Note that class-based animations are treated differently compared to structural animations
14695 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14696 * depending if CSS or JavaScript animations are used.
14698 * @param {DOMElement} element the element which the CSS classes will be applied to
14699 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
14700 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
14701 * @param {object=} options an optional collection of options/styles that will be applied to the element
14703 * @return {Promise} the animation callback promise
14705 setClass: function(element, add, remove, options) {
14706 options = prepareAnimateOptions(options);
14707 options.addClass = mergeClasses(options.addClass, add);
14708 options.removeClass = mergeClasses(options.removeClass, remove);
14709 return $$animateQueue.push(element, 'setClass', options);
14714 * @name $animate#animate
14717 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
14718 * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
14719 * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
14720 * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
14721 * style in `to`, the style in `from` is applied immediately, and no animation is run.
14722 * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
14723 * method (or as part of the `options` parameter):
14726 * ngModule.animation('.my-inline-animation', function() {
14728 * animate : function(element, from, to, done, options) {
14736 * @param {DOMElement} element the element which the CSS styles will be applied to
14737 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
14738 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
14739 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
14740 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
14741 * (Note that if no animation is detected then this value will not be applied to the element.)
14742 * @param {object=} options an optional collection of options/styles that will be applied to the element
14744 * @return {Promise} the animation callback promise
14746 animate: function(element, from, to, className, options) {
14747 options = prepareAnimateOptions(options);
14748 options.from = options.from ? extend(options.from, from) : from;
14749 options.to = options.to ? extend(options.to, to) : to;
14751 className = className || 'ng-inline-animate';
14752 options.tempClasses = mergeClasses(options.tempClasses, className);
14753 return $$animateQueue.push(element, 'animate', options);
14759 var $$AnimateAsyncRunFactoryProvider = function() {
14760 this.$get = ['$$rAF', function($$rAF) {
14761 var waitQueue = [];
14763 function waitForTick(fn) {
14764 waitQueue.push(fn);
14765 if (waitQueue.length > 1) return;
14767 for (var i = 0; i < waitQueue.length; i++) {
14774 return function() {
14775 var passed = false;
14776 waitForTick(function() {
14779 return function(callback) {
14780 passed ? callback() : waitForTick(callback);
14786 var $$AnimateRunnerFactoryProvider = function() {
14787 this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
14788 function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
14790 var INITIAL_STATE = 0;
14791 var DONE_PENDING_STATE = 1;
14792 var DONE_COMPLETE_STATE = 2;
14794 AnimateRunner.chain = function(chain, callback) {
14799 if (index === chain.length) {
14804 chain[index](function(response) {
14805 if (response === false) {
14815 AnimateRunner.all = function(runners, callback) {
14818 forEach(runners, function(runner) {
14819 runner.done(onProgress);
14822 function onProgress(response) {
14823 status = status && response;
14824 if (++count === runners.length) {
14830 function AnimateRunner(host) {
14831 this.setHost(host);
14833 var rafTick = $$animateAsyncRun();
14834 var timeoutTick = function(fn) {
14835 $timeout(fn, 0, false);
14838 this._doneCallbacks = [];
14839 this._tick = function(fn) {
14840 var doc = $document[0];
14842 // the document may not be ready or attached
14843 // to the module for some internal tests
14844 if (doc && doc.hidden) {
14853 AnimateRunner.prototype = {
14854 setHost: function(host) {
14855 this.host = host || {};
14858 done: function(fn) {
14859 if (this._state === DONE_COMPLETE_STATE) {
14862 this._doneCallbacks.push(fn);
14868 getPromise: function() {
14869 if (!this.promise) {
14871 this.promise = $q(function(resolve, reject) {
14872 self.done(function(status) {
14873 status === false ? reject() : resolve();
14877 return this.promise;
14880 then: function(resolveHandler, rejectHandler) {
14881 return this.getPromise().then(resolveHandler, rejectHandler);
14884 'catch': function(handler) {
14885 return this.getPromise()['catch'](handler);
14888 'finally': function(handler) {
14889 return this.getPromise()['finally'](handler);
14892 pause: function() {
14893 if (this.host.pause) {
14898 resume: function() {
14899 if (this.host.resume) {
14900 this.host.resume();
14905 if (this.host.end) {
14908 this._resolve(true);
14911 cancel: function() {
14912 if (this.host.cancel) {
14913 this.host.cancel();
14915 this._resolve(false);
14918 complete: function(response) {
14920 if (self._state === INITIAL_STATE) {
14921 self._state = DONE_PENDING_STATE;
14922 self._tick(function() {
14923 self._resolve(response);
14928 _resolve: function(response) {
14929 if (this._state !== DONE_COMPLETE_STATE) {
14930 forEach(this._doneCallbacks, function(fn) {
14933 this._doneCallbacks.length = 0;
14934 this._state = DONE_COMPLETE_STATE;
14939 return AnimateRunner;
14945 * @name $animateCss
14949 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
14950 * then the `$animateCss` service will actually perform animations.
14952 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
14954 var $CoreAnimateCssProvider = function() {
14955 this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
14957 return function(element, initialOptions) {
14958 // all of the animation functions should create
14959 // a copy of the options data, however, if a
14960 // parent service has already created a copy then
14961 // we should stick to using that
14962 var options = initialOptions || {};
14963 if (!options.$$prepared) {
14964 options = copy(options);
14967 // there is no point in applying the styles since
14968 // there is no animation that goes on at all in
14969 // this version of $animateCss.
14970 if (options.cleanupStyles) {
14971 options.from = options.to = null;
14974 if (options.from) {
14975 element.css(options.from);
14976 options.from = null;
14979 /* jshint newcap: false */
14980 var closed, runner = new $$AnimateRunner();
14988 applyAnimationContents();
14997 function applyAnimationContents() {
14998 if (options.addClass) {
14999 element.addClass(options.addClass);
15000 options.addClass = null;
15002 if (options.removeClass) {
15003 element.removeClass(options.removeClass);
15004 options.removeClass = null;
15007 element.css(options.to);
15015 /* global stripHash: true */
15018 * ! This is a private undocumented service !
15023 * This object has two goals:
15025 * - hide all the global state in the browser caused by the window object
15026 * - abstract away all the browser specific features and inconsistencies
15028 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
15029 * service, which can be used for convenient testing of the application without the interaction with
15030 * the real browser apis.
15033 * @param {object} window The global window object.
15034 * @param {object} document jQuery wrapped document.
15035 * @param {object} $log window.console or an object with the same interface.
15036 * @param {object} $sniffer $sniffer service
15038 function Browser(window, document, $log, $sniffer) {
15040 location = window.location,
15041 history = window.history,
15042 setTimeout = window.setTimeout,
15043 clearTimeout = window.clearTimeout,
15044 pendingDeferIds = {};
15046 self.isMock = false;
15048 var outstandingRequestCount = 0;
15049 var outstandingRequestCallbacks = [];
15051 // TODO(vojta): remove this temporary api
15052 self.$$completeOutstandingRequest = completeOutstandingRequest;
15053 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
15056 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
15057 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
15059 function completeOutstandingRequest(fn) {
15061 fn.apply(null, sliceArgs(arguments, 1));
15063 outstandingRequestCount--;
15064 if (outstandingRequestCount === 0) {
15065 while (outstandingRequestCallbacks.length) {
15067 outstandingRequestCallbacks.pop()();
15076 function getHash(url) {
15077 var index = url.indexOf('#');
15078 return index === -1 ? '' : url.substr(index);
15083 * Note: this method is used only by scenario runner
15084 * TODO(vojta): prefix this method with $$ ?
15085 * @param {function()} callback Function that will be called when no outstanding request
15087 self.notifyWhenNoOutstandingRequests = function(callback) {
15088 if (outstandingRequestCount === 0) {
15091 outstandingRequestCallbacks.push(callback);
15095 //////////////////////////////////////////////////////////////
15097 //////////////////////////////////////////////////////////////
15099 var cachedState, lastHistoryState,
15100 lastBrowserUrl = location.href,
15101 baseElement = document.find('base'),
15102 pendingLocation = null,
15103 getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
15105 return history.state;
15107 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
15112 lastHistoryState = cachedState;
15115 * @name $browser#url
15119 * Without any argument, this method just returns current value of location.href.
15122 * With at least one argument, this method sets url to new value.
15123 * If html5 history api supported, pushState/replaceState is used, otherwise
15124 * location.href/location.replace is used.
15125 * Returns its own instance to allow chaining
15127 * NOTE: this api is intended for use only by the $location service. Please use the
15128 * {@link ng.$location $location service} to change url.
15130 * @param {string} url New url (when used as setter)
15131 * @param {boolean=} replace Should new url replace current history record?
15132 * @param {object=} state object to use with pushState/replaceState
15134 self.url = function(url, replace, state) {
15135 // In modern browsers `history.state` is `null` by default; treating it separately
15136 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
15137 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
15138 if (isUndefined(state)) {
15142 // Android Browser BFCache causes location, history reference to become stale.
15143 if (location !== window.location) location = window.location;
15144 if (history !== window.history) history = window.history;
15148 var sameState = lastHistoryState === state;
15150 // Don't change anything if previous and current URLs and states match. This also prevents
15151 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
15152 // See https://github.com/angular/angular.js/commit/ffb2701
15153 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
15156 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
15157 lastBrowserUrl = url;
15158 lastHistoryState = state;
15159 // Don't use history API if only the hash changed
15160 // due to a bug in IE10/IE11 which leads
15161 // to not firing a `hashchange` nor `popstate` event
15162 // in some cases (see #9143).
15163 if ($sniffer.history && (!sameBase || !sameState)) {
15164 history[replace ? 'replaceState' : 'pushState'](state, '', url);
15166 // Do the assignment again so that those two variables are referentially identical.
15167 lastHistoryState = cachedState;
15169 if (!sameBase || pendingLocation) {
15170 pendingLocation = url;
15173 location.replace(url);
15174 } else if (!sameBase) {
15175 location.href = url;
15177 location.hash = getHash(url);
15179 if (location.href !== url) {
15180 pendingLocation = url;
15186 // - pendingLocation is needed as browsers don't allow to read out
15187 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
15188 // https://openradar.appspot.com/22186109).
15189 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
15190 return pendingLocation || location.href.replace(/%27/g,"'");
15195 * @name $browser#state
15198 * This method is a getter.
15200 * Return history.state or null if history.state is undefined.
15202 * @returns {object} state
15204 self.state = function() {
15205 return cachedState;
15208 var urlChangeListeners = [],
15209 urlChangeInit = false;
15211 function cacheStateAndFireUrlChange() {
15212 pendingLocation = null;
15217 // This variable should be used *only* inside the cacheState function.
15218 var lastCachedState = null;
15219 function cacheState() {
15220 // This should be the only place in $browser where `history.state` is read.
15221 cachedState = getCurrentState();
15222 cachedState = isUndefined(cachedState) ? null : cachedState;
15224 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
15225 if (equals(cachedState, lastCachedState)) {
15226 cachedState = lastCachedState;
15228 lastCachedState = cachedState;
15231 function fireUrlChange() {
15232 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
15236 lastBrowserUrl = self.url();
15237 lastHistoryState = cachedState;
15238 forEach(urlChangeListeners, function(listener) {
15239 listener(self.url(), cachedState);
15244 * @name $browser#onUrlChange
15247 * Register callback function that will be called, when url changes.
15249 * It's only called when the url is changed from outside of angular:
15250 * - user types different url into address bar
15251 * - user clicks on history (forward/back) button
15252 * - user clicks on a link
15254 * It's not called when url is changed by $browser.url() method
15256 * The listener gets called with new url as parameter.
15258 * NOTE: this api is intended for use only by the $location service. Please use the
15259 * {@link ng.$location $location service} to monitor url changes in angular apps.
15261 * @param {function(string)} listener Listener function to be called when url changes.
15262 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
15264 self.onUrlChange = function(callback) {
15265 // TODO(vojta): refactor to use node's syntax for events
15266 if (!urlChangeInit) {
15267 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
15268 // don't fire popstate when user change the address bar and don't fire hashchange when url
15269 // changed by push/replaceState
15271 // html5 history api - popstate event
15272 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
15273 // hashchange event
15274 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
15276 urlChangeInit = true;
15279 urlChangeListeners.push(callback);
15285 * Remove popstate and hashchange handler from window.
15287 * NOTE: this api is intended for use only by $rootScope.
15289 self.$$applicationDestroyed = function() {
15290 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
15294 * Checks whether the url has changed outside of Angular.
15295 * Needs to be exported to be able to check for changes that have been done in sync,
15296 * as hashchange/popstate events fire in async.
15298 self.$$checkUrlChange = fireUrlChange;
15300 //////////////////////////////////////////////////////////////
15302 //////////////////////////////////////////////////////////////
15305 * @name $browser#baseHref
15308 * Returns current <base href>
15309 * (always relative - without domain)
15311 * @returns {string} The current base href
15313 self.baseHref = function() {
15314 var href = baseElement.attr('href');
15315 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
15319 * @name $browser#defer
15320 * @param {function()} fn A function, who's execution should be deferred.
15321 * @param {number=} [delay=0] of milliseconds to defer the function execution.
15322 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
15325 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
15327 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
15328 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
15329 * via `$browser.defer.flush()`.
15332 self.defer = function(fn, delay) {
15334 outstandingRequestCount++;
15335 timeoutId = setTimeout(function() {
15336 delete pendingDeferIds[timeoutId];
15337 completeOutstandingRequest(fn);
15339 pendingDeferIds[timeoutId] = true;
15345 * @name $browser#defer.cancel
15348 * Cancels a deferred task identified with `deferId`.
15350 * @param {*} deferId Token returned by the `$browser.defer` function.
15351 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
15354 self.defer.cancel = function(deferId) {
15355 if (pendingDeferIds[deferId]) {
15356 delete pendingDeferIds[deferId];
15357 clearTimeout(deferId);
15358 completeOutstandingRequest(noop);
15366 function $BrowserProvider() {
15367 this.$get = ['$window', '$log', '$sniffer', '$document',
15368 function($window, $log, $sniffer, $document) {
15369 return new Browser($window, $document, $log, $sniffer);
15375 * @name $cacheFactory
15378 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
15383 * var cache = $cacheFactory('cacheId');
15384 * expect($cacheFactory.get('cacheId')).toBe(cache);
15385 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
15387 * cache.put("key", "value");
15388 * cache.put("another key", "another value");
15390 * // We've specified no options on creation
15391 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
15396 * @param {string} cacheId Name or id of the newly created cache.
15397 * @param {object=} options Options object that specifies the cache behavior. Properties:
15399 * - `{number=}` `capacity` — turns the cache into LRU cache.
15401 * @returns {object} Newly created cache object with the following set of methods:
15403 * - `{object}` `info()` — Returns id, size, and options of cache.
15404 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
15406 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
15407 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
15408 * - `{void}` `removeAll()` — Removes all cached values.
15409 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
15412 <example module="cacheExampleApp">
15413 <file name="index.html">
15414 <div ng-controller="CacheController">
15415 <input ng-model="newCacheKey" placeholder="Key">
15416 <input ng-model="newCacheValue" placeholder="Value">
15417 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
15419 <p ng-if="keys.length">Cached Values</p>
15420 <div ng-repeat="key in keys">
15421 <span ng-bind="key"></span>
15423 <b ng-bind="cache.get(key)"></b>
15427 <div ng-repeat="(key, value) in cache.info()">
15428 <span ng-bind="key"></span>
15430 <b ng-bind="value"></b>
15434 <file name="script.js">
15435 angular.module('cacheExampleApp', []).
15436 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
15438 $scope.cache = $cacheFactory('cacheId');
15439 $scope.put = function(key, value) {
15440 if (angular.isUndefined($scope.cache.get(key))) {
15441 $scope.keys.push(key);
15443 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
15447 <file name="style.css">
15449 margin: 10px 0 3px;
15454 function $CacheFactoryProvider() {
15456 this.$get = function() {
15459 function cacheFactory(cacheId, options) {
15460 if (cacheId in caches) {
15461 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
15465 stats = extend({}, options, {id: cacheId}),
15466 data = createMap(),
15467 capacity = (options && options.capacity) || Number.MAX_VALUE,
15468 lruHash = createMap(),
15474 * @name $cacheFactory.Cache
15477 * A cache object used to store and retrieve data, primarily used by
15478 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
15479 * templates and other data.
15482 * angular.module('superCache')
15483 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
15484 * return $cacheFactory('super-cache');
15491 * it('should behave like a cache', inject(function(superCache) {
15492 * superCache.put('key', 'value');
15493 * superCache.put('another key', 'another value');
15495 * expect(superCache.info()).toEqual({
15496 * id: 'super-cache',
15500 * superCache.remove('another key');
15501 * expect(superCache.get('another key')).toBeUndefined();
15503 * superCache.removeAll();
15504 * expect(superCache.info()).toEqual({
15505 * id: 'super-cache',
15511 return caches[cacheId] = {
15515 * @name $cacheFactory.Cache#put
15519 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
15520 * retrieved later, and incrementing the size of the cache if the key was not already
15521 * present in the cache. If behaving like an LRU cache, it will also remove stale
15522 * entries from the set.
15524 * It will not insert undefined values into the cache.
15526 * @param {string} key the key under which the cached data is stored.
15527 * @param {*} value the value to store alongside the key. If it is undefined, the key
15528 * will not be stored.
15529 * @returns {*} the value stored.
15531 put: function(key, value) {
15532 if (isUndefined(value)) return;
15533 if (capacity < Number.MAX_VALUE) {
15534 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
15539 if (!(key in data)) size++;
15542 if (size > capacity) {
15543 this.remove(staleEnd.key);
15551 * @name $cacheFactory.Cache#get
15555 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
15557 * @param {string} key the key of the data to be retrieved
15558 * @returns {*} the value stored.
15560 get: function(key) {
15561 if (capacity < Number.MAX_VALUE) {
15562 var lruEntry = lruHash[key];
15564 if (!lruEntry) return;
15575 * @name $cacheFactory.Cache#remove
15579 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
15581 * @param {string} key the key of the entry to be removed
15583 remove: function(key) {
15584 if (capacity < Number.MAX_VALUE) {
15585 var lruEntry = lruHash[key];
15587 if (!lruEntry) return;
15589 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
15590 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
15591 link(lruEntry.n,lruEntry.p);
15593 delete lruHash[key];
15596 if (!(key in data)) return;
15605 * @name $cacheFactory.Cache#removeAll
15609 * Clears the cache object of any entries.
15611 removeAll: function() {
15612 data = createMap();
15614 lruHash = createMap();
15615 freshEnd = staleEnd = null;
15621 * @name $cacheFactory.Cache#destroy
15625 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
15626 * removing it from the {@link $cacheFactory $cacheFactory} set.
15628 destroy: function() {
15632 delete caches[cacheId];
15638 * @name $cacheFactory.Cache#info
15642 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
15644 * @returns {object} an object with the following properties:
15646 * <li>**id**: the id of the cache instance</li>
15647 * <li>**size**: the number of entries kept in the cache instance</li>
15648 * <li>**...**: any additional properties from the options object when creating the
15653 return extend({}, stats, {size: size});
15659 * makes the `entry` the freshEnd of the LRU linked list
15661 function refresh(entry) {
15662 if (entry != freshEnd) {
15665 } else if (staleEnd == entry) {
15666 staleEnd = entry.n;
15669 link(entry.n, entry.p);
15670 link(entry, freshEnd);
15678 * bidirectionally links two entries of the LRU linked list
15680 function link(nextEntry, prevEntry) {
15681 if (nextEntry != prevEntry) {
15682 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
15683 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
15691 * @name $cacheFactory#info
15694 * Get information about all the caches that have been created
15696 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
15698 cacheFactory.info = function() {
15700 forEach(caches, function(cache, cacheId) {
15701 info[cacheId] = cache.info();
15709 * @name $cacheFactory#get
15712 * Get access to a cache object by the `cacheId` used when it was created.
15714 * @param {string} cacheId Name or id of a cache to access.
15715 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
15717 cacheFactory.get = function(cacheId) {
15718 return caches[cacheId];
15722 return cacheFactory;
15728 * @name $templateCache
15731 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
15732 * can load templates directly into the cache in a `script` tag, or by consuming the
15733 * `$templateCache` service directly.
15735 * Adding via the `script` tag:
15738 * <script type="text/ng-template" id="templateId.html">
15739 * <p>This is the content of the template</p>
15743 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
15744 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
15745 * element with ng-app attribute), otherwise the template will be ignored.
15747 * Adding via the `$templateCache` service:
15750 * var myApp = angular.module('myApp', []);
15751 * myApp.run(function($templateCache) {
15752 * $templateCache.put('templateId.html', 'This is the content of the template');
15756 * To retrieve the template later, simply use it in your HTML:
15758 * <div ng-include=" 'templateId.html' "></div>
15761 * or get it via Javascript:
15763 * $templateCache.get('templateId.html')
15766 * See {@link ng.$cacheFactory $cacheFactory}.
15769 function $TemplateCacheProvider() {
15770 this.$get = ['$cacheFactory', function($cacheFactory) {
15771 return $cacheFactory('templates');
15775 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15776 * Any commits to this file should be reviewed with security in mind. *
15777 * Changes to this file can potentially create security vulnerabilities. *
15778 * An approval from 2 Core members with history of modifying *
15779 * this file is required. *
15781 * Does the change somehow allow for arbitrary javascript to be executed? *
15782 * Or allows for someone to change the prototype of built-in objects? *
15783 * Or gives undesired access to variables likes document or window? *
15784 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15786 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
15788 * DOM-related variables:
15790 * - "node" - DOM Node
15791 * - "element" - DOM Element or Node
15792 * - "$node" or "$element" - jqLite-wrapped node or element
15795 * Compiler related stuff:
15797 * - "linkFn" - linking fn of a single directive
15798 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
15799 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
15800 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
15810 * Compiles an HTML string or DOM into a template and produces a template function, which
15811 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
15813 * The compilation is a process of walking the DOM tree and matching DOM elements to
15814 * {@link ng.$compileProvider#directive directives}.
15816 * <div class="alert alert-warning">
15817 * **Note:** This document is an in-depth reference of all directive options.
15818 * For a gentle introduction to directives with examples of common use cases,
15819 * see the {@link guide/directive directive guide}.
15822 * ## Comprehensive Directive API
15824 * There are many different options for a directive.
15826 * The difference resides in the return value of the factory function.
15827 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
15828 * or just the `postLink` function (all other properties will have the default values).
15830 * <div class="alert alert-success">
15831 * **Best Practice:** It's recommended to use the "directive definition object" form.
15834 * Here's an example directive declared with a Directive Definition Object:
15837 * var myModule = angular.module(...);
15839 * myModule.directive('directiveName', function factory(injectables) {
15840 * var directiveDefinitionObject = {
15842 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
15844 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
15845 * transclude: false,
15847 * templateNamespace: 'html',
15849 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
15850 * controllerAs: 'stringIdentifier',
15851 * bindToController: false,
15852 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
15853 * compile: function compile(tElement, tAttrs, transclude) {
15855 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
15856 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
15859 * // return function postLink( ... ) { ... }
15863 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
15864 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
15867 * // link: function postLink( ... ) { ... }
15869 * return directiveDefinitionObject;
15873 * <div class="alert alert-warning">
15874 * **Note:** Any unspecified options will use the default value. You can see the default values below.
15877 * Therefore the above can be simplified as:
15880 * var myModule = angular.module(...);
15882 * myModule.directive('directiveName', function factory(injectables) {
15883 * var directiveDefinitionObject = {
15884 * link: function postLink(scope, iElement, iAttrs) { ... }
15886 * return directiveDefinitionObject;
15888 * // return function postLink(scope, iElement, iAttrs) { ... }
15894 * ### Directive Definition Object
15896 * The directive definition object provides instructions to the {@link ng.$compile
15897 * compiler}. The attributes are:
15899 * #### `multiElement`
15900 * When this property is set to true, the HTML compiler will collect DOM nodes between
15901 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
15902 * together as the directive elements. It is recommended that this feature be used on directives
15903 * which are not strictly behavioral (such as {@link ngClick}), and which
15904 * do not manipulate or replace child nodes (such as {@link ngInclude}).
15907 * When there are multiple directives defined on a single DOM element, sometimes it
15908 * is necessary to specify the order in which the directives are applied. The `priority` is used
15909 * to sort the directives before their `compile` functions get called. Priority is defined as a
15910 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
15911 * are also run in priority order, but post-link functions are run in reverse order. The order
15912 * of directives with the same priority is undefined. The default priority is `0`.
15915 * If set to true then the current `priority` will be the last set of directives
15916 * which will execute (any directives at the current priority will still execute
15917 * as the order of execution on same `priority` is undefined). Note that expressions
15918 * and other directives used in the directive's template will also be excluded from execution.
15921 * The scope property can be `true`, an object or a falsy value:
15923 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
15925 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
15926 * the directive's element. If multiple directives on the same element request a new scope,
15927 * only one new scope is created. The new scope rule does not apply for the root of the template
15928 * since the root of the template always gets a new scope.
15930 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
15931 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
15932 * scope. This is useful when creating reusable components, which should not accidentally read or modify
15933 * data in the parent scope.
15935 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
15936 * directive's element. These local properties are useful for aliasing values for templates. The keys in
15937 * the object hash map to the name of the property on the isolate scope; the values define how the property
15938 * is bound to the parent scope, via matching attributes on the directive's element:
15940 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
15941 * always a string since DOM attributes are strings. If no `attr` name is specified then the
15942 * attribute name is assumed to be the same as the local name. Given `<my-component
15943 * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
15944 * the directive's scope property `localName` will reflect the interpolated value of `hello
15945 * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
15946 * scope. The `name` is read from the parent scope (not the directive's scope).
15948 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
15949 * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
15950 * If no `attr` name is specified then the attribute name is assumed to be the same as the local
15951 * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
15952 * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
15953 * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
15954 * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
15955 * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
15956 * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
15957 * will be thrown upon discovering changes to the local value, since it will be impossible to sync
15958 * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
15959 * method is used for tracking changes, and the equality check is based on object identity.
15960 * However, if an object literal or an array literal is passed as the binding expression, the
15961 * equality check is done by value (using the {@link angular.equals} function). It's also possible
15962 * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
15963 * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
15965 * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
15966 * expression passed via the attribute `attr`. The expression is evaluated in the context of the
15967 * parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
15968 * local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
15970 * For example, given `<my-component my-attr="parentModel">` and directive definition of
15971 * `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
15972 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
15973 * in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
15975 * 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
15976 * sets the same value. That means if your bound value is an object, changes to its properties
15977 * in the isolated scope will be reflected in the parent scope (because both reference the same object).
15978 * 2. one-way binding watches changes to the **identity** of the parent value. That means the
15979 * {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
15980 * to the value has changed. In most cases, this should not be of concern, but can be important
15981 * to know if you one-way bind to an object, and then replace that object in the isolated scope.
15982 * If you now change a property of the object in your parent scope, the change will not be
15983 * propagated to the isolated scope, because the identity of the object on the parent scope
15984 * has not changed. Instead you must assign a new object.
15986 * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
15987 * back to the parent. However, it does not make this completely impossible.
15989 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
15990 * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
15991 * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
15992 * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
15993 * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
15994 * via an expression to the parent scope. This can be done by passing a map of local variable names
15995 * and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
15996 * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
15998 * In general it's possible to apply more than one directive to one element, but there might be limitations
15999 * depending on the type of scope required by the directives. The following points will help explain these limitations.
16000 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
16002 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
16003 * * **child scope** + **no scope** => Both directives will share one single child scope
16004 * * **child scope** + **child scope** => Both directives will share one single child scope
16005 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
16006 * its parent's scope
16007 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
16008 * be applied to the same element.
16009 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
16010 * cannot be applied to the same element.
16013 * #### `bindToController`
16014 * This property is used to bind scope properties directly to the controller. It can be either
16015 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
16016 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
16017 * definition: `controller: 'myCtrl as myAlias'`.
16019 * When an isolate scope is used for a directive (see above), `bindToController: true` will
16020 * allow a component to have its properties bound to the controller, rather than to scope.
16022 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
16023 * properties. You can access these bindings once they have been initialized by providing a controller method called
16024 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
16027 * <div class="alert alert-warning">
16028 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
16029 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
16030 * code that relies upon bindings inside a `$onInit` method on the controller, instead.
16033 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
16034 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
16035 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
16036 * scope (useful for component directives).
16038 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
16041 * #### `controller`
16042 * Controller constructor function. The controller is instantiated before the
16043 * pre-linking phase and can be accessed by other directives (see
16044 * `require` attribute). This allows the directives to communicate with each other and augment
16045 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
16047 * * `$scope` - Current scope associated with the element
16048 * * `$element` - Current element
16049 * * `$attrs` - Current attributes object for the element
16050 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
16051 * `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
16052 * * `scope`: (optional) override the scope.
16053 * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
16054 * * `futureParentElement` (optional):
16055 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
16056 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
16057 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
16058 * and when the `cloneLinkinFn` is passed,
16059 * as those elements need to created and cloned in a special way when they are defined outside their
16060 * usual containers (e.g. like `<svg>`).
16061 * * See also the `directive.templateNamespace` property.
16062 * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
16063 * then the default translusion is provided.
16064 * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
16065 * `true` if the specified slot contains content (i.e. one or more DOM nodes).
16067 * The controller can provide the following methods that act as life-cycle hooks:
16068 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
16069 * had their bindings initialized (and before the pre & post linking functions for the directives on
16070 * this element). This is a good place to put initialization code for your controller.
16071 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
16072 * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
16073 * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
16074 * component such as cloning the bound value to prevent accidental mutation of the outer value.
16075 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
16076 * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
16077 * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
16078 * components will have their `$onDestroy()` hook called before child components.
16079 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
16080 * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
16081 * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
16082 * they are waiting for their template to load asynchronously and their own compilation and linking has been
16083 * suspended until that occurs.
16087 * Require another directive and inject its controller as the fourth argument to the linking function. The
16088 * `require` property can be a string, an array or an object:
16089 * * a **string** containing the name of the directive to pass to the linking function
16090 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
16091 * linking function will be an array of controllers in the same order as the names in the `require` property
16092 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
16093 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
16096 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
16097 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
16098 * have been constructed but before `$onInit` is called.
16099 * See the {@link $compileProvider#component} helper for an example of how this can be used.
16101 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
16102 * raised (unless no link function is specified and the required controllers are not being bound to the directive
16103 * controller, in which case error checking is skipped). The name can be prefixed with:
16105 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
16106 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
16107 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
16108 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
16109 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
16110 * `null` to the `link` fn if not found.
16111 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
16112 * `null` to the `link` fn if not found.
16115 * #### `controllerAs`
16116 * Identifier name for a reference to the controller in the directive's scope.
16117 * This allows the controller to be referenced from the directive template. This is especially
16118 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
16119 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
16120 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
16124 * String of subset of `EACM` which restricts the directive to a specific directive
16125 * declaration style. If omitted, the defaults (elements and attributes) are used.
16127 * * `E` - Element name (default): `<my-directive></my-directive>`
16128 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
16129 * * `C` - Class: `<div class="my-directive: exp;"></div>`
16130 * * `M` - Comment: `<!-- directive: my-directive exp -->`
16133 * #### `templateNamespace`
16134 * String representing the document type used by the markup in the template.
16135 * AngularJS needs this information as those elements need to be created and cloned
16136 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
16138 * * `html` - All root nodes in the template are HTML. Root nodes may also be
16139 * top-level elements such as `<svg>` or `<math>`.
16140 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
16141 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
16143 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
16146 * HTML markup that may:
16147 * * Replace the contents of the directive's element (default).
16148 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
16149 * * Wrap the contents of the directive's element (if `transclude` is true).
16153 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
16154 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
16155 * function api below) and returns a string value.
16158 * #### `templateUrl`
16159 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
16161 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
16162 * for later when the template has been resolved. In the meantime it will continue to compile and link
16163 * sibling and parent elements as though this element had not contained any directives.
16165 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
16166 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
16167 * case when only one deeply nested directive has `templateUrl`.
16169 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
16171 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
16172 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
16173 * a string value representing the url. In either case, the template URL is passed through {@link
16174 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
16177 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
16178 * specify what the template should replace. Defaults to `false`.
16180 * * `true` - the template will replace the directive's element.
16181 * * `false` - the template will replace the contents of the directive's element.
16183 * The replacement process migrates all of the attributes / classes from the old element to the new
16184 * one. See the {@link guide/directive#template-expanding-directive
16185 * Directives Guide} for an example.
16187 * There are very few scenarios where element replacement is required for the application function,
16188 * the main one being reusable custom components that are used within SVG contexts
16189 * (because SVG doesn't work with custom elements in the DOM tree).
16191 * #### `transclude`
16192 * Extract the contents of the element where the directive appears and make it available to the directive.
16193 * The contents are compiled and provided to the directive as a **transclusion function**. See the
16194 * {@link $compile#transclusion Transclusion} section below.
16200 * function compile(tElement, tAttrs, transclude) { ... }
16203 * The compile function deals with transforming the template DOM. Since most directives do not do
16204 * template transformation, it is not used often. The compile function takes the following arguments:
16206 * * `tElement` - template element - The element where the directive has been declared. It is
16207 * safe to do template transformation on the element and child elements only.
16209 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
16210 * between all directive compile functions.
16212 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
16214 * <div class="alert alert-warning">
16215 * **Note:** The template instance and the link instance may be different objects if the template has
16216 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
16217 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
16218 * should be done in a linking function rather than in a compile function.
16221 * <div class="alert alert-warning">
16222 * **Note:** The compile function cannot handle directives that recursively use themselves in their
16223 * own templates or compile functions. Compiling these directives results in an infinite loop and
16224 * stack overflow errors.
16226 * This can be avoided by manually using $compile in the postLink function to imperatively compile
16227 * a directive's template instead of relying on automatic template compilation via `template` or
16228 * `templateUrl` declaration or manual compilation inside the compile function.
16231 * <div class="alert alert-danger">
16232 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
16233 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
16234 * to the link function instead.
16237 * A compile function can have a return value which can be either a function or an object.
16239 * * returning a (post-link) function - is equivalent to registering the linking function via the
16240 * `link` property of the config object when the compile function is empty.
16242 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
16243 * control when a linking function should be called during the linking phase. See info about
16244 * pre-linking and post-linking functions below.
16248 * This property is used only if the `compile` property is not defined.
16251 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
16254 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
16255 * executed after the template has been cloned. This is where most of the directive logic will be
16258 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
16259 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
16261 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
16262 * manipulate the children of the element only in `postLink` function since the children have
16263 * already been linked.
16265 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
16266 * between all directive linking functions.
16268 * * `controller` - the directive's required controller instance(s) - Instances are shared
16269 * among all directives, which allows the directives to use the controllers as a communication
16270 * channel. The exact value depends on the directive's `require` property:
16271 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
16272 * * `string`: the controller instance
16273 * * `array`: array of controller instances
16275 * If a required controller cannot be found, and it is optional, the instance is `null`,
16276 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
16278 * Note that you can also require the directive's own controller - it will be made available like
16279 * any other controller.
16281 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
16282 * This is the same as the `$transclude`
16283 * parameter of directive controllers, see there for details.
16284 * `function([scope], cloneLinkingFn, futureParentElement)`.
16286 * #### Pre-linking function
16288 * Executed before the child elements are linked. Not safe to do DOM transformation since the
16289 * compiler linking function will fail to locate the correct elements for linking.
16291 * #### Post-linking function
16293 * Executed after the child elements are linked.
16295 * Note that child elements that contain `templateUrl` directives will not have been compiled
16296 * and linked since they are waiting for their template to load asynchronously and their own
16297 * compilation and linking has been suspended until that occurs.
16299 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
16300 * for their async templates to be resolved.
16305 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
16306 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
16307 * scope from where they were taken.
16309 * Transclusion is used (often with {@link ngTransclude}) to insert the
16310 * original contents of a directive's element into a specified place in the template of the directive.
16311 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
16312 * content has access to the properties on the scope from which it was taken, even if the directive
16313 * has isolated scope.
16314 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
16316 * This makes it possible for the widget to have private state for its template, while the transcluded
16317 * content has access to its originating scope.
16319 * <div class="alert alert-warning">
16320 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
16321 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
16322 * Testing Transclusion Directives}.
16325 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
16326 * directive's element, the entire element or multiple parts of the element contents:
16328 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
16329 * * `'element'` - transclude the whole of the directive's element including any directives on this
16330 * element that defined at a lower priority than this directive. When used, the `template`
16331 * property is ignored.
16332 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
16334 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
16336 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
16337 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
16338 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
16340 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
16342 * If the element selector is prefixed with a `?` then that slot is optional.
16344 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
16345 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
16347 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
16348 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
16349 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
16350 * injectable into the directive's controller.
16353 * #### Transclusion Functions
16355 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
16356 * function** to the directive's `link` function and `controller`. This transclusion function is a special
16357 * **linking function** that will return the compiled contents linked to a new transclusion scope.
16359 * <div class="alert alert-info">
16360 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
16361 * ngTransclude will deal with it for us.
16364 * If you want to manually control the insertion and removal of the transcluded content in your directive
16365 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
16366 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
16368 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
16369 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
16370 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
16372 * <div class="alert alert-info">
16373 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
16374 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
16377 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
16378 * attach function**:
16381 * var transcludedContent, transclusionScope;
16383 * $transclude(function(clone, scope) {
16384 * element.append(clone);
16385 * transcludedContent = clone;
16386 * transclusionScope = scope;
16390 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
16391 * associated transclusion scope:
16394 * transcludedContent.remove();
16395 * transclusionScope.$destroy();
16398 * <div class="alert alert-info">
16399 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
16400 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
16401 * then you are also responsible for calling `$destroy` on the transclusion scope.
16404 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
16405 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
16406 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
16409 * #### Transclusion Scopes
16411 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
16412 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
16413 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
16416 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
16422 * <div transclusion>
16428 * The `$parent` scope hierarchy will look like this:
16436 * but the scopes will inherit prototypically from different scopes to their `$parent`.
16447 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
16448 * `link()` or `compile()` functions. It has a variety of uses.
16450 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
16451 * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
16452 * to the attributes.
16454 * * *Directive inter-communication:* All directives share the same instance of the attributes
16455 * object which allows the directives to use the attributes object as inter directive
16458 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
16459 * allowing other directives to read the interpolated value.
16461 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
16462 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
16463 * the only way to easily get the actual value because during the linking phase the interpolation
16464 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
16467 * function linkingFn(scope, elm, attrs, ctrl) {
16468 * // get the attribute value
16469 * console.log(attrs.ngModel);
16471 * // change the attribute
16472 * attrs.$set('ngModel', 'new value');
16474 * // observe changes to interpolated attribute
16475 * attrs.$observe('ngModel', function(value) {
16476 * console.log('ngModel has changed value to ' + value);
16483 * <div class="alert alert-warning">
16484 * **Note**: Typically directives are registered with `module.directive`. The example below is
16485 * to illustrate how `$compile` works.
16488 <example module="compileExample">
16489 <file name="index.html">
16491 angular.module('compileExample', [], function($compileProvider) {
16492 // configure new 'compile' directive by passing a directive
16493 // factory function. The factory function injects the '$compile'
16494 $compileProvider.directive('compile', function($compile) {
16495 // directive factory creates a link function
16496 return function(scope, element, attrs) {
16499 // watch the 'compile' expression for changes
16500 return scope.$eval(attrs.compile);
16503 // when the 'compile' expression changes
16504 // assign it into the current DOM
16505 element.html(value);
16507 // compile the new DOM and link it to the current
16509 // NOTE: we only compile .childNodes so that
16510 // we don't get into infinite loop compiling ourselves
16511 $compile(element.contents())(scope);
16517 .controller('GreeterController', ['$scope', function($scope) {
16518 $scope.name = 'Angular';
16519 $scope.html = 'Hello {{name}}';
16522 <div ng-controller="GreeterController">
16523 <input ng-model="name"> <br/>
16524 <textarea ng-model="html"></textarea> <br/>
16525 <div compile="html"></div>
16528 <file name="protractor.js" type="protractor">
16529 it('should auto compile', function() {
16530 var textarea = $('textarea');
16531 var output = $('div[compile]');
16532 // The initial state reads 'Hello Angular'.
16533 expect(output.getText()).toBe('Hello Angular');
16535 textarea.sendKeys('{{name}}!');
16536 expect(output.getText()).toBe('Angular!');
16543 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
16544 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
16546 * <div class="alert alert-danger">
16547 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
16548 * e.g. will not use the right outer scope. Please pass the transclude function as a
16549 * `parentBoundTranscludeFn` to the link function instead.
16552 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
16553 * root element(s), not their children)
16554 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
16555 * (a DOM element/tree) to a scope. Where:
16557 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
16558 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
16559 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
16560 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
16561 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
16563 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
16564 * * `scope` - is the current scope with which the linking function is working with.
16566 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
16567 * keys may be used to control linking behavior:
16569 * * `parentBoundTranscludeFn` - the transclude function made available to
16570 * directives; if given, it will be passed through to the link functions of
16571 * directives found in `element` during compilation.
16572 * * `transcludeControllers` - an object hash with keys that map controller names
16573 * to a hash with the key `instance`, which maps to the controller instance;
16574 * if given, it will make the controllers available to directives on the compileNode:
16578 * instance: parentControllerInstance
16582 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
16583 * the cloned elements; only needed for transcludes that are allowed to contain non html
16584 * elements (e.g. SVG elements). See also the directive.controller property.
16586 * Calling the linking function returns the element of the template. It is either the original
16587 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
16589 * After linking the view is not updated until after a call to $digest which typically is done by
16590 * Angular automatically.
16592 * If you need access to the bound view, there are two ways to do it:
16594 * - If you are not asking the linking function to clone the template, create the DOM element(s)
16595 * before you send them to the compiler and keep this reference around.
16597 * var element = $compile('<p>{{total}}</p>')(scope);
16600 * - if on the other hand, you need the element to be cloned, the view reference from the original
16601 * example would not point to the clone, but rather to the original template that was cloned. In
16602 * this case, you can access the clone via the cloneAttachFn:
16604 * var templateElement = angular.element('<p>{{total}}</p>'),
16607 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
16608 * //attach the clone to DOM document at the right place
16611 * //now we have reference to the cloned DOM via `clonedElement`
16615 * For information on how the compiler works, see the
16616 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
16619 var $compileMinErr = minErr('$compile');
16621 function UNINITIALIZED_VALUE() {}
16622 var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
16626 * @name $compileProvider
16630 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
16631 function $CompileProvider($provide, $$sanitizeUriProvider) {
16632 var hasDirectives = {},
16633 Suffix = 'Directive',
16634 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
16635 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
16636 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
16637 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
16639 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
16640 // The assumption is that future DOM event attribute names will begin with
16641 // 'on' and be composed of only English letters.
16642 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
16643 var bindingCache = createMap();
16645 function parseIsolateBindings(scope, directiveName, isController) {
16646 var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
16648 var bindings = createMap();
16650 forEach(scope, function(definition, scopeName) {
16651 if (definition in bindingCache) {
16652 bindings[scopeName] = bindingCache[definition];
16655 var match = definition.match(LOCAL_REGEXP);
16658 throw $compileMinErr('iscp',
16659 "Invalid {3} for directive '{0}'." +
16660 " Definition: {... {1}: '{2}' ...}",
16661 directiveName, scopeName, definition,
16662 (isController ? "controller bindings definition" :
16663 "isolate scope definition"));
16666 bindings[scopeName] = {
16668 collection: match[2] === '*',
16669 optional: match[3] === '?',
16670 attrName: match[4] || scopeName
16673 bindingCache[definition] = bindings[scopeName];
16680 function parseDirectiveBindings(directive, directiveName) {
16682 isolateScope: null,
16683 bindToController: null
16685 if (isObject(directive.scope)) {
16686 if (directive.bindToController === true) {
16687 bindings.bindToController = parseIsolateBindings(directive.scope,
16688 directiveName, true);
16689 bindings.isolateScope = {};
16691 bindings.isolateScope = parseIsolateBindings(directive.scope,
16692 directiveName, false);
16695 if (isObject(directive.bindToController)) {
16696 bindings.bindToController =
16697 parseIsolateBindings(directive.bindToController, directiveName, true);
16699 if (isObject(bindings.bindToController)) {
16700 var controller = directive.controller;
16701 var controllerAs = directive.controllerAs;
16703 // There is no controller, there may or may not be a controllerAs property
16704 throw $compileMinErr('noctrl',
16705 "Cannot bind to controller without directive '{0}'s controller.",
16707 } else if (!identifierForController(controller, controllerAs)) {
16708 // There is a controller, but no identifier or controllerAs property
16709 throw $compileMinErr('noident',
16710 "Cannot bind to controller without identifier for directive '{0}'.",
16717 function assertValidDirectiveName(name) {
16718 var letter = name.charAt(0);
16719 if (!letter || letter !== lowercase(letter)) {
16720 throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
16722 if (name !== name.trim()) {
16723 throw $compileMinErr('baddir',
16724 "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
16731 * @name $compileProvider#directive
16735 * Register a new directive with the compiler.
16737 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
16738 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
16739 * names and the values are the factories.
16740 * @param {Function|Array} directiveFactory An injectable directive factory function. See the
16741 * {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
16742 * @returns {ng.$compileProvider} Self for chaining.
16744 this.directive = function registerDirective(name, directiveFactory) {
16745 assertNotHasOwnProperty(name, 'directive');
16746 if (isString(name)) {
16747 assertValidDirectiveName(name);
16748 assertArg(directiveFactory, 'directiveFactory');
16749 if (!hasDirectives.hasOwnProperty(name)) {
16750 hasDirectives[name] = [];
16751 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
16752 function($injector, $exceptionHandler) {
16753 var directives = [];
16754 forEach(hasDirectives[name], function(directiveFactory, index) {
16756 var directive = $injector.invoke(directiveFactory);
16757 if (isFunction(directive)) {
16758 directive = { compile: valueFn(directive) };
16759 } else if (!directive.compile && directive.link) {
16760 directive.compile = valueFn(directive.link);
16762 directive.priority = directive.priority || 0;
16763 directive.index = index;
16764 directive.name = directive.name || name;
16765 directive.require = directive.require || (directive.controller && directive.name);
16766 directive.restrict = directive.restrict || 'EA';
16767 directive.$$moduleName = directiveFactory.$$moduleName;
16768 directives.push(directive);
16770 $exceptionHandler(e);
16776 hasDirectives[name].push(directiveFactory);
16778 forEach(name, reverseParams(registerDirective));
16785 * @name $compileProvider#component
16787 * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
16788 * @param {Object} options Component definition object (a simplified
16789 * {@link ng.$compile#directive-definition-object directive definition object}),
16790 * with the following properties (all optional):
16792 * - `controller` – `{(string|function()=}` – controller constructor function that should be
16793 * associated with newly created scope or the name of a {@link ng.$compile#-controller-
16794 * registered controller} if passed as a string. An empty `noop` function by default.
16795 * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
16796 * If present, the controller will be published to scope under the `controllerAs` name.
16797 * If not present, this will default to be `$ctrl`.
16798 * - `template` – `{string=|function()=}` – html template as a string or a function that
16799 * returns an html template as a string which should be used as the contents of this component.
16800 * Empty string by default.
16802 * If `template` is a function, then it is {@link auto.$injector#invoke injected} with
16803 * the following locals:
16805 * - `$element` - Current element
16806 * - `$attrs` - Current attributes object for the element
16808 * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
16809 * template that should be used as the contents of this component.
16811 * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
16812 * the following locals:
16814 * - `$element` - Current element
16815 * - `$attrs` - Current attributes object for the element
16817 * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
16818 * Component properties are always bound to the component controller and not to the scope.
16819 * See {@link ng.$compile#-bindtocontroller- `bindToController`}.
16820 * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
16821 * Disabled by default.
16822 * - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
16823 * this component's controller. The object keys specify the property names under which the required
16824 * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
16825 * - `$...` – additional properties to attach to the directive factory function and the controller
16826 * constructor function. (This is used by the component router to annotate)
16828 * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
16830 * Register a **component definition** with the compiler. This is a shorthand for registering a special
16831 * type of directive, which represents a self-contained UI component in your application. Such components
16832 * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
16834 * Component definitions are very simple and do not require as much configuration as defining general
16835 * directives. Component definitions usually consist only of a template and a controller backing it.
16837 * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
16838 * `bindToController`. They always have **isolate scope** and are restricted to elements.
16840 * Here are a few examples of how you would usually define components:
16843 * var myMod = angular.module(...);
16844 * myMod.component('myComp', {
16845 * template: '<div>My name is {{$ctrl.name}}</div>',
16846 * controller: function() {
16847 * this.name = 'shahar';
16851 * myMod.component('myComp', {
16852 * template: '<div>My name is {{$ctrl.name}}</div>',
16853 * bindings: {name: '@'}
16856 * myMod.component('myComp', {
16857 * templateUrl: 'views/my-comp.html',
16858 * controller: 'MyCtrl',
16859 * controllerAs: 'ctrl',
16860 * bindings: {name: '@'}
16864 * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
16867 * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
16869 this.component = function registerComponent(name, options) {
16870 var controller = options.controller || function() {};
16872 function factory($injector) {
16873 function makeInjectable(fn) {
16874 if (isFunction(fn) || isArray(fn)) {
16875 return function(tElement, tAttrs) {
16876 return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
16883 var template = (!options.template && !options.templateUrl ? '' : options.template);
16885 controller: controller,
16886 controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
16887 template: makeInjectable(template),
16888 templateUrl: makeInjectable(options.templateUrl),
16889 transclude: options.transclude,
16891 bindToController: options.bindings || {},
16893 require: options.require
16896 // Copy annotations (starting with $) over to the DDO
16897 forEach(options, function(val, key) {
16898 if (key.charAt(0) === '$') ddo[key] = val;
16904 // TODO(pete) remove the following `forEach` before we release 1.6.0
16905 // The component-router@0.2.0 looks for the annotations on the controller constructor
16906 // Nothing in Angular looks for annotations on the factory function but we can't remove
16907 // it from 1.5.x yet.
16909 // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
16910 // These could be used by libraries such as the new component router
16911 forEach(options, function(val, key) {
16912 if (key.charAt(0) === '$') {
16913 factory[key] = val;
16914 // Don't try to copy over annotations to named controller
16915 if (isFunction(controller)) controller[key] = val;
16919 factory.$inject = ['$injector'];
16921 return this.directive(name, factory);
16927 * @name $compileProvider#aHrefSanitizationWhitelist
16931 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16932 * urls during a[href] sanitization.
16934 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
16936 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16937 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16938 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16939 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16941 * @param {RegExp=} regexp New regexp to whitelist urls with.
16942 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16943 * chaining otherwise.
16945 this.aHrefSanitizationWhitelist = function(regexp) {
16946 if (isDefined(regexp)) {
16947 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
16950 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
16957 * @name $compileProvider#imgSrcSanitizationWhitelist
16961 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16962 * urls during img[src] sanitization.
16964 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16966 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16967 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16968 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16969 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16971 * @param {RegExp=} regexp New regexp to whitelist urls with.
16972 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16973 * chaining otherwise.
16975 this.imgSrcSanitizationWhitelist = function(regexp) {
16976 if (isDefined(regexp)) {
16977 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
16980 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
16986 * @name $compileProvider#debugInfoEnabled
16988 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
16989 * current debugInfoEnabled state
16990 * @returns {*} current value if used as getter or itself (chaining) if used as setter
16995 * Call this method to enable/disable various debug runtime information in the compiler such as adding
16996 * binding information and a reference to the current scope on to DOM elements.
16997 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
16998 * * `ng-binding` CSS class
16999 * * `$binding` data property containing an array of the binding expressions
17001 * You may want to disable this in production for a significant performance boost. See
17002 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
17004 * The default value is true.
17006 var debugInfoEnabled = true;
17007 this.debugInfoEnabled = function(enabled) {
17008 if (isDefined(enabled)) {
17009 debugInfoEnabled = enabled;
17012 return debugInfoEnabled;
17019 * @name $compileProvider#onChangesTtl
17022 * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
17023 * assuming that the model is unstable.
17025 * The current default is 10 iterations.
17027 * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
17028 * in several iterations of calls to these hooks. However if an application needs more than the default 10
17029 * iterations to stabilize then you should investigate what is causing the model to continuously change during
17030 * the `$onChanges` hook execution.
17032 * Increasing the TTL could have performance implications, so you should not change it without proper justification.
17034 * @param {number} limit The number of `$onChanges` hook iterations.
17035 * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
17037 this.onChangesTtl = function(value) {
17038 if (arguments.length) {
17046 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
17047 '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
17048 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
17049 $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
17051 var SIMPLE_ATTR_NAME = /^\w/;
17052 var specialAttrHolder = window.document.createElement('div');
17056 var onChangesTtl = TTL;
17057 // The onChanges hooks should all be run together in a single digest
17058 // When changes occur, the call to trigger their hooks will be added to this queue
17059 var onChangesQueue;
17061 // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
17062 function flushOnChangesQueue() {
17064 if (!(--onChangesTtl)) {
17065 // We have hit the TTL limit so reset everything
17066 onChangesQueue = undefined;
17067 throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
17069 // We must run this hook in an apply since the $$postDigest runs outside apply
17070 $rootScope.$apply(function() {
17071 for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
17072 onChangesQueue[i]();
17074 // Reset the queue to trigger a new schedule next time there is a change
17075 onChangesQueue = undefined;
17083 function Attributes(element, attributesToCopy) {
17084 if (attributesToCopy) {
17085 var keys = Object.keys(attributesToCopy);
17088 for (i = 0, l = keys.length; i < l; i++) {
17090 this[key] = attributesToCopy[key];
17096 this.$$element = element;
17099 Attributes.prototype = {
17102 * @name $compile.directive.Attributes#$normalize
17106 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
17107 * `data-`) to its normalized, camelCase form.
17109 * Also there is special case for Moz prefix starting with upper case letter.
17111 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
17113 * @param {string} name Name to normalize
17115 $normalize: directiveNormalize,
17120 * @name $compile.directive.Attributes#$addClass
17124 * Adds the CSS class value specified by the classVal parameter to the element. If animations
17125 * are enabled then an animation will be triggered for the class addition.
17127 * @param {string} classVal The className value that will be added to the element
17129 $addClass: function(classVal) {
17130 if (classVal && classVal.length > 0) {
17131 $animate.addClass(this.$$element, classVal);
17137 * @name $compile.directive.Attributes#$removeClass
17141 * Removes the CSS class value specified by the classVal parameter from the element. If
17142 * animations are enabled then an animation will be triggered for the class removal.
17144 * @param {string} classVal The className value that will be removed from the element
17146 $removeClass: function(classVal) {
17147 if (classVal && classVal.length > 0) {
17148 $animate.removeClass(this.$$element, classVal);
17154 * @name $compile.directive.Attributes#$updateClass
17158 * Adds and removes the appropriate CSS class values to the element based on the difference
17159 * between the new and old CSS class values (specified as newClasses and oldClasses).
17161 * @param {string} newClasses The current CSS className value
17162 * @param {string} oldClasses The former CSS className value
17164 $updateClass: function(newClasses, oldClasses) {
17165 var toAdd = tokenDifference(newClasses, oldClasses);
17166 if (toAdd && toAdd.length) {
17167 $animate.addClass(this.$$element, toAdd);
17170 var toRemove = tokenDifference(oldClasses, newClasses);
17171 if (toRemove && toRemove.length) {
17172 $animate.removeClass(this.$$element, toRemove);
17177 * Set a normalized attribute on the element in a way such that all directives
17178 * can share the attribute. This function properly handles boolean attributes.
17179 * @param {string} key Normalized key. (ie ngAttribute)
17180 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
17181 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
17182 * Defaults to true.
17183 * @param {string=} attrName Optional none normalized name. Defaults to key.
17185 $set: function(key, value, writeAttr, attrName) {
17186 // TODO: decide whether or not to throw an error if "class"
17187 //is set through this function since it may cause $updateClass to
17190 var node = this.$$element[0],
17191 booleanKey = getBooleanAttrName(node, key),
17192 aliasedKey = getAliasedAttrName(key),
17197 this.$$element.prop(key, value);
17198 attrName = booleanKey;
17199 } else if (aliasedKey) {
17200 this[aliasedKey] = value;
17201 observer = aliasedKey;
17206 // translate normalized key to actual key
17208 this.$attr[key] = attrName;
17210 attrName = this.$attr[key];
17212 this.$attr[key] = attrName = snake_case(key, '-');
17216 nodeName = nodeName_(this.$$element);
17218 if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
17219 (nodeName === 'img' && key === 'src')) {
17220 // sanitize a[href] and img[src] values
17221 this[key] = value = $$sanitizeUri(value, key === 'src');
17222 } else if (nodeName === 'img' && key === 'srcset') {
17223 // sanitize img[srcset] values
17226 // first check if there are spaces because it's not the same pattern
17227 var trimmedSrcset = trim(value);
17228 // ( 999x ,| 999w ,| ,|, )
17229 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
17230 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
17232 // split srcset into tuple of uri and descriptor except for the last item
17233 var rawUris = trimmedSrcset.split(pattern);
17236 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
17237 for (var i = 0; i < nbrUrisWith2parts; i++) {
17238 var innerIdx = i * 2;
17239 // sanitize the uri
17240 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
17241 // add the descriptor
17242 result += (" " + trim(rawUris[innerIdx + 1]));
17245 // split the last item into uri and descriptor
17246 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
17248 // sanitize the last uri
17249 result += $$sanitizeUri(trim(lastTuple[0]), true);
17251 // and add the last descriptor if any
17252 if (lastTuple.length === 2) {
17253 result += (" " + trim(lastTuple[1]));
17255 this[key] = value = result;
17258 if (writeAttr !== false) {
17259 if (value === null || isUndefined(value)) {
17260 this.$$element.removeAttr(attrName);
17262 if (SIMPLE_ATTR_NAME.test(attrName)) {
17263 this.$$element.attr(attrName, value);
17265 setSpecialAttr(this.$$element[0], attrName, value);
17271 var $$observers = this.$$observers;
17272 $$observers && forEach($$observers[observer], function(fn) {
17276 $exceptionHandler(e);
17284 * @name $compile.directive.Attributes#$observe
17288 * Observes an interpolated attribute.
17290 * The observer function will be invoked once during the next `$digest` following
17291 * compilation. The observer is then invoked whenever the interpolated value
17294 * @param {string} key Normalized key. (ie ngAttribute) .
17295 * @param {function(interpolatedValue)} fn Function that will be called whenever
17296 the interpolated value of the attribute changes.
17297 * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
17298 * guide} for more info.
17299 * @returns {function()} Returns a deregistration function for this observer.
17301 $observe: function(key, fn) {
17303 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
17304 listeners = ($$observers[key] || ($$observers[key] = []));
17306 listeners.push(fn);
17307 $rootScope.$evalAsync(function() {
17308 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
17309 // no one registered attribute interpolation function, so lets call it manually
17314 return function() {
17315 arrayRemove(listeners, fn);
17320 function setSpecialAttr(element, attrName, value) {
17321 // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
17322 // so we have to jump through some hoops to get such an attribute
17323 // https://github.com/angular/angular.js/pull/13318
17324 specialAttrHolder.innerHTML = "<span " + attrName + ">";
17325 var attributes = specialAttrHolder.firstChild.attributes;
17326 var attribute = attributes[0];
17327 // We have to remove the attribute from its container element before we can add it to the destination element
17328 attributes.removeNamedItem(attribute.name);
17329 attribute.value = value;
17330 element.attributes.setNamedItem(attribute);
17333 function safeAddClass($element, className) {
17335 $element.addClass(className);
17337 // ignore, since it means that we are trying to set class on
17338 // SVG element, where class name is read-only.
17343 var startSymbol = $interpolate.startSymbol(),
17344 endSymbol = $interpolate.endSymbol(),
17345 denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
17347 : function denormalizeTemplate(template) {
17348 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
17350 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
17351 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
17353 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
17354 var bindings = $element.data('$binding') || [];
17356 if (isArray(binding)) {
17357 bindings = bindings.concat(binding);
17359 bindings.push(binding);
17362 $element.data('$binding', bindings);
17365 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
17366 safeAddClass($element, 'ng-binding');
17369 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
17370 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
17371 $element.data(dataName, scope);
17374 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
17375 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
17378 compile.$$createComment = function(directiveName, comment) {
17380 if (debugInfoEnabled) {
17381 content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
17383 return window.document.createComment(content);
17388 //================================
17390 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
17391 previousCompileContext) {
17392 if (!($compileNodes instanceof jqLite)) {
17393 // jquery always rewraps, whereas we need to preserve the original selector so that we can
17395 $compileNodes = jqLite($compileNodes);
17398 var NOT_EMPTY = /\S+/;
17400 // We can not compile top level text elements since text nodes can be merged and we will
17401 // not be able to attach scope data to them, so we will wrap them in <span>
17402 for (var i = 0, len = $compileNodes.length; i < len; i++) {
17403 var domNode = $compileNodes[i];
17405 if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
17406 jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
17410 var compositeLinkFn =
17411 compileNodes($compileNodes, transcludeFn, $compileNodes,
17412 maxPriority, ignoreDirective, previousCompileContext);
17413 compile.$$addScopeClass($compileNodes);
17414 var namespace = null;
17415 return function publicLinkFn(scope, cloneConnectFn, options) {
17416 assertArg(scope, 'scope');
17418 if (previousCompileContext && previousCompileContext.needsNewScope) {
17419 // A parent directive did a replace and a directive on this element asked
17420 // for transclusion, which caused us to lose a layer of element on which
17421 // we could hold the new transclusion scope, so we will create it manually
17423 scope = scope.$parent.$new();
17426 options = options || {};
17427 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
17428 transcludeControllers = options.transcludeControllers,
17429 futureParentElement = options.futureParentElement;
17431 // When `parentBoundTranscludeFn` is passed, it is a
17432 // `controllersBoundTransclude` function (it was previously passed
17433 // as `transclude` to directive.link) so we must unwrap it to get
17434 // its `boundTranscludeFn`
17435 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
17436 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
17440 namespace = detectNamespaceForChildElements(futureParentElement);
17443 if (namespace !== 'html') {
17444 // When using a directive with replace:true and templateUrl the $compileNodes
17445 // (or a child element inside of them)
17446 // might change, so we need to recreate the namespace adapted compileNodes
17447 // for call to the link function.
17448 // Note: This will already clone the nodes...
17449 $linkNode = jqLite(
17450 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
17452 } else if (cloneConnectFn) {
17453 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
17454 // and sometimes changes the structure of the DOM.
17455 $linkNode = JQLitePrototype.clone.call($compileNodes);
17457 $linkNode = $compileNodes;
17460 if (transcludeControllers) {
17461 for (var controllerName in transcludeControllers) {
17462 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
17466 compile.$$addScopeInfo($linkNode, scope);
17468 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
17469 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
17474 function detectNamespaceForChildElements(parentElement) {
17475 // TODO: Make this detect MathML as well...
17476 var node = parentElement && parentElement[0];
17480 return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
17485 * Compile function matches each node in nodeList against the directives. Once all directives
17486 * for a particular node are collected their compile functions are executed. The compile
17487 * functions return values - the linking functions - are combined into a composite linking
17488 * function, which is the a linking function for the node.
17490 * @param {NodeList} nodeList an array of nodes or NodeList to compile
17491 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
17492 * scope argument is auto-generated to the new child of the transcluded parent scope.
17493 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
17494 * the rootElement must be set the jqLite collection of the compile root. This is
17495 * needed so that the jqLite collection items can be replaced with widgets.
17496 * @param {number=} maxPriority Max directive priority.
17497 * @returns {Function} A composite linking function of all of the matched directives or null.
17499 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
17500 previousCompileContext) {
17502 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
17504 for (var i = 0; i < nodeList.length; i++) {
17505 attrs = new Attributes();
17507 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
17508 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
17511 nodeLinkFn = (directives.length)
17512 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
17513 null, [], [], previousCompileContext)
17516 if (nodeLinkFn && nodeLinkFn.scope) {
17517 compile.$$addScopeClass(attrs.$$element);
17520 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
17521 !(childNodes = nodeList[i].childNodes) ||
17522 !childNodes.length)
17524 : compileNodes(childNodes,
17526 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
17527 && nodeLinkFn.transclude) : transcludeFn);
17529 if (nodeLinkFn || childLinkFn) {
17530 linkFns.push(i, nodeLinkFn, childLinkFn);
17531 linkFnFound = true;
17532 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
17535 //use the previous context only for the first element in the virtual group
17536 previousCompileContext = null;
17539 // return a linking function if we have found anything, null otherwise
17540 return linkFnFound ? compositeLinkFn : null;
17542 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
17543 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
17544 var stableNodeList;
17547 if (nodeLinkFnFound) {
17548 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
17549 // offsets don't get screwed up
17550 var nodeListLength = nodeList.length;
17551 stableNodeList = new Array(nodeListLength);
17553 // create a sparse array by only copying the elements which have a linkFn
17554 for (i = 0; i < linkFns.length; i+=3) {
17556 stableNodeList[idx] = nodeList[idx];
17559 stableNodeList = nodeList;
17562 for (i = 0, ii = linkFns.length; i < ii;) {
17563 node = stableNodeList[linkFns[i++]];
17564 nodeLinkFn = linkFns[i++];
17565 childLinkFn = linkFns[i++];
17568 if (nodeLinkFn.scope) {
17569 childScope = scope.$new();
17570 compile.$$addScopeInfo(jqLite(node), childScope);
17572 childScope = scope;
17575 if (nodeLinkFn.transcludeOnThisElement) {
17576 childBoundTranscludeFn = createBoundTranscludeFn(
17577 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
17579 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
17580 childBoundTranscludeFn = parentBoundTranscludeFn;
17582 } else if (!parentBoundTranscludeFn && transcludeFn) {
17583 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
17586 childBoundTranscludeFn = null;
17589 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
17591 } else if (childLinkFn) {
17592 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
17598 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
17599 function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
17601 if (!transcludedScope) {
17602 transcludedScope = scope.$new(false, containingScope);
17603 transcludedScope.$$transcluded = true;
17606 return transcludeFn(transcludedScope, cloneFn, {
17607 parentBoundTranscludeFn: previousBoundTranscludeFn,
17608 transcludeControllers: controllers,
17609 futureParentElement: futureParentElement
17613 // We need to attach the transclusion slots onto the `boundTranscludeFn`
17614 // so that they are available inside the `controllersBoundTransclude` function
17615 var boundSlots = boundTranscludeFn.$$slots = createMap();
17616 for (var slotName in transcludeFn.$$slots) {
17617 if (transcludeFn.$$slots[slotName]) {
17618 boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
17620 boundSlots[slotName] = null;
17624 return boundTranscludeFn;
17628 * Looks for directives on the given node and adds them to the directive collection which is
17631 * @param node Node to search.
17632 * @param directives An array to which the directives are added to. This array is sorted before
17633 * the function returns.
17634 * @param attrs The shared attrs object which is used to populate the normalized attributes.
17635 * @param {number=} maxPriority Max directive priority.
17637 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
17638 var nodeType = node.nodeType,
17639 attrsMap = attrs.$attr,
17643 switch (nodeType) {
17644 case NODE_TYPE_ELEMENT: /* Element */
17645 // use the node name: <directive>
17646 addDirective(directives,
17647 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
17649 // iterate over the attributes
17650 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
17651 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
17652 var attrStartName = false;
17653 var attrEndName = false;
17657 value = trim(attr.value);
17659 // support ngAttr attribute binding
17660 ngAttrName = directiveNormalize(name);
17661 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
17662 name = name.replace(PREFIX_REGEXP, '')
17663 .substr(8).replace(/_(.)/g, function(match, letter) {
17664 return letter.toUpperCase();
17668 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
17669 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
17670 attrStartName = name;
17671 attrEndName = name.substr(0, name.length - 5) + 'end';
17672 name = name.substr(0, name.length - 6);
17675 nName = directiveNormalize(name.toLowerCase());
17676 attrsMap[nName] = name;
17677 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
17678 attrs[nName] = value;
17679 if (getBooleanAttrName(node, nName)) {
17680 attrs[nName] = true; // presence means true
17683 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
17684 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
17688 // use class as directive
17689 className = node.className;
17690 if (isObject(className)) {
17691 // Maybe SVGAnimatedString
17692 className = className.animVal;
17694 if (isString(className) && className !== '') {
17695 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
17696 nName = directiveNormalize(match[2]);
17697 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
17698 attrs[nName] = trim(match[3]);
17700 className = className.substr(match.index + match[0].length);
17704 case NODE_TYPE_TEXT: /* Text Node */
17706 // Workaround for #11781
17707 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
17708 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
17709 node.parentNode.removeChild(node.nextSibling);
17712 addTextInterpolateDirective(directives, node.nodeValue);
17714 case NODE_TYPE_COMMENT: /* Comment */
17716 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
17718 nName = directiveNormalize(match[1]);
17719 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
17720 attrs[nName] = trim(match[2]);
17724 // turns out that under some circumstances IE9 throws errors when one attempts to read
17725 // comment's node value.
17726 // Just ignore it and continue. (Can't seem to reproduce in test case.)
17731 directives.sort(byPriority);
17736 * Given a node with an directive-start it collects all of the siblings until it finds
17743 function groupScan(node, attrStart, attrEnd) {
17746 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
17749 throw $compileMinErr('uterdir',
17750 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
17751 attrStart, attrEnd);
17753 if (node.nodeType == NODE_TYPE_ELEMENT) {
17754 if (node.hasAttribute(attrStart)) depth++;
17755 if (node.hasAttribute(attrEnd)) depth--;
17758 node = node.nextSibling;
17759 } while (depth > 0);
17764 return jqLite(nodes);
17768 * Wrapper for linking function which converts normal linking function into a grouped
17769 * linking function.
17773 * @returns {Function}
17775 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
17776 return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
17777 element = groupScan(element[0], attrStart, attrEnd);
17778 return linkFn(scope, element, attrs, controllers, transcludeFn);
17783 * A function generator that is used to support both eager and lazy compilation
17784 * linking function.
17786 * @param $compileNodes
17787 * @param transcludeFn
17788 * @param maxPriority
17789 * @param ignoreDirective
17790 * @param previousCompileContext
17791 * @returns {Function}
17793 function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
17797 return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17799 return function lazyCompilation() {
17801 compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17803 // Null out all of these references in order to make them eligible for garbage collection
17804 // since this is a potentially long lived closure
17805 $compileNodes = transcludeFn = previousCompileContext = null;
17807 return compiled.apply(this, arguments);
17812 * Once the directives have been collected, their compile functions are executed. This method
17813 * is responsible for inlining directive templates as well as terminating the application
17814 * of the directives if the terminal directive has been reached.
17816 * @param {Array} directives Array of collected directives to execute their compile function.
17817 * this needs to be pre-sorted by priority order.
17818 * @param {Node} compileNode The raw DOM node to apply the compile functions to
17819 * @param {Object} templateAttrs The shared attribute function
17820 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
17821 * scope argument is auto-generated to the new
17822 * child of the transcluded parent scope.
17823 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
17824 * argument has the root jqLite array so that we can replace nodes
17826 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
17827 * compiling the transclusion.
17828 * @param {Array.<Function>} preLinkFns
17829 * @param {Array.<Function>} postLinkFns
17830 * @param {Object} previousCompileContext Context used for previous compilation of the current
17832 * @returns {Function} linkFn
17834 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
17835 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
17836 previousCompileContext) {
17837 previousCompileContext = previousCompileContext || {};
17839 var terminalPriority = -Number.MAX_VALUE,
17840 newScopeDirective = previousCompileContext.newScopeDirective,
17841 controllerDirectives = previousCompileContext.controllerDirectives,
17842 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
17843 templateDirective = previousCompileContext.templateDirective,
17844 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
17845 hasTranscludeDirective = false,
17846 hasTemplate = false,
17847 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
17848 $compileNode = templateAttrs.$$element = jqLite(compileNode),
17852 replaceDirective = originalReplaceDirective,
17853 childTranscludeFn = transcludeFn,
17855 didScanForMultipleTransclusion = false,
17856 mightHaveMultipleTransclusionError = false,
17859 // executes all directives on the current element
17860 for (var i = 0, ii = directives.length; i < ii; i++) {
17861 directive = directives[i];
17862 var attrStart = directive.$$start;
17863 var attrEnd = directive.$$end;
17865 // collect multiblock sections
17867 $compileNode = groupScan(compileNode, attrStart, attrEnd);
17869 $template = undefined;
17871 if (terminalPriority > directive.priority) {
17872 break; // prevent further processing of directives
17875 if (directiveValue = directive.scope) {
17877 // skip the check for directives with async templates, we'll check the derived sync
17878 // directive when the template arrives
17879 if (!directive.templateUrl) {
17880 if (isObject(directiveValue)) {
17881 // This directive is trying to add an isolated scope.
17882 // Check that there is no scope of any kind already
17883 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
17884 directive, $compileNode);
17885 newIsolateScopeDirective = directive;
17887 // This directive is trying to add a child scope.
17888 // Check that there is no isolated scope already
17889 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
17894 newScopeDirective = newScopeDirective || directive;
17897 directiveName = directive.name;
17899 // If we encounter a condition that can result in transclusion on the directive,
17900 // then scan ahead in the remaining directives for others that may cause a multiple
17901 // transclusion error to be thrown during the compilation process. If a matching directive
17902 // is found, then we know that when we encounter a transcluded directive, we need to eagerly
17903 // compile the `transclude` function rather than doing it lazily in order to throw
17904 // exceptions at the correct time
17905 if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
17906 || (directive.transclude && !directive.$$tlb))) {
17907 var candidateDirective;
17909 for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
17910 if ((candidateDirective.transclude && !candidateDirective.$$tlb)
17911 || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
17912 mightHaveMultipleTransclusionError = true;
17917 didScanForMultipleTransclusion = true;
17920 if (!directive.templateUrl && directive.controller) {
17921 directiveValue = directive.controller;
17922 controllerDirectives = controllerDirectives || createMap();
17923 assertNoDuplicate("'" + directiveName + "' controller",
17924 controllerDirectives[directiveName], directive, $compileNode);
17925 controllerDirectives[directiveName] = directive;
17928 if (directiveValue = directive.transclude) {
17929 hasTranscludeDirective = true;
17931 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
17932 // This option should only be used by directives that know how to safely handle element transclusion,
17933 // where the transcluded nodes are added or replaced after linking.
17934 if (!directive.$$tlb) {
17935 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
17936 nonTlbTranscludeDirective = directive;
17939 if (directiveValue == 'element') {
17940 hasElementTranscludeDirective = true;
17941 terminalPriority = directive.priority;
17942 $template = $compileNode;
17943 $compileNode = templateAttrs.$$element =
17944 jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
17945 compileNode = $compileNode[0];
17946 replaceWith(jqCollection, sliceArgs($template), compileNode);
17948 // Support: Chrome < 50
17949 // https://github.com/angular/angular.js/issues/14041
17951 // In the versions of V8 prior to Chrome 50, the document fragment that is created
17952 // in the `replaceWith` function is improperly garbage collected despite still
17953 // being referenced by the `parentNode` property of all of the child nodes. By adding
17954 // a reference to the fragment via a different property, we can avoid that incorrect
17956 // TODO: remove this line after Chrome 50 has been released
17957 $template[0].$$parentNode = $template[0].parentNode;
17959 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
17960 replaceDirective && replaceDirective.name, {
17962 // - controllerDirectives - otherwise we'll create duplicates controllers
17963 // - newIsolateScopeDirective or templateDirective - combining templates with
17964 // element transclusion doesn't make sense.
17966 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
17967 // on the same element more than once.
17968 nonTlbTranscludeDirective: nonTlbTranscludeDirective
17972 var slots = createMap();
17974 $template = jqLite(jqLiteClone(compileNode)).contents();
17976 if (isObject(directiveValue)) {
17978 // We have transclusion slots,
17979 // collect them up, compile them and store their transclusion functions
17982 var slotMap = createMap();
17983 var filledSlots = createMap();
17985 // Parse the element selectors
17986 forEach(directiveValue, function(elementSelector, slotName) {
17987 // If an element selector starts with a ? then it is optional
17988 var optional = (elementSelector.charAt(0) === '?');
17989 elementSelector = optional ? elementSelector.substring(1) : elementSelector;
17991 slotMap[elementSelector] = slotName;
17993 // We explicitly assign `null` since this implies that a slot was defined but not filled.
17994 // Later when calling boundTransclusion functions with a slot name we only error if the
17995 // slot is `undefined`
17996 slots[slotName] = null;
17998 // filledSlots contains `true` for all slots that are either optional or have been
17999 // filled. This is used to check that we have not missed any required slots
18000 filledSlots[slotName] = optional;
18003 // Add the matching elements into their slot
18004 forEach($compileNode.contents(), function(node) {
18005 var slotName = slotMap[directiveNormalize(nodeName_(node))];
18007 filledSlots[slotName] = true;
18008 slots[slotName] = slots[slotName] || [];
18009 slots[slotName].push(node);
18011 $template.push(node);
18015 // Check for required slots that were not filled
18016 forEach(filledSlots, function(filled, slotName) {
18018 throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
18022 for (var slotName in slots) {
18023 if (slots[slotName]) {
18024 // Only define a transclusion function if the slot was filled
18025 slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
18030 $compileNode.empty(); // clear contents
18031 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
18032 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
18033 childTranscludeFn.$$slots = slots;
18037 if (directive.template) {
18038 hasTemplate = true;
18039 assertNoDuplicate('template', templateDirective, directive, $compileNode);
18040 templateDirective = directive;
18042 directiveValue = (isFunction(directive.template))
18043 ? directive.template($compileNode, templateAttrs)
18044 : directive.template;
18046 directiveValue = denormalizeTemplate(directiveValue);
18048 if (directive.replace) {
18049 replaceDirective = directive;
18050 if (jqLiteIsTextNode(directiveValue)) {
18053 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
18055 compileNode = $template[0];
18057 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
18058 throw $compileMinErr('tplrt',
18059 "Template for directive '{0}' must have exactly one root element. {1}",
18060 directiveName, '');
18063 replaceWith(jqCollection, $compileNode, compileNode);
18065 var newTemplateAttrs = {$attr: {}};
18067 // combine directives from the original node and from the template:
18068 // - take the array of directives for this element
18069 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
18070 // - collect directives from the template and sort them by priority
18071 // - combine directives as: processed + template + unprocessed
18072 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
18073 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
18075 if (newIsolateScopeDirective || newScopeDirective) {
18076 // The original directive caused the current element to be replaced but this element
18077 // also needs to have a new scope, so we need to tell the template directives
18078 // that they would need to get their scope from further up, if they require transclusion
18079 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
18081 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
18082 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
18084 ii = directives.length;
18086 $compileNode.html(directiveValue);
18090 if (directive.templateUrl) {
18091 hasTemplate = true;
18092 assertNoDuplicate('template', templateDirective, directive, $compileNode);
18093 templateDirective = directive;
18095 if (directive.replace) {
18096 replaceDirective = directive;
18100 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
18102 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
18103 controllerDirectives: controllerDirectives,
18104 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
18105 newIsolateScopeDirective: newIsolateScopeDirective,
18106 templateDirective: templateDirective,
18107 nonTlbTranscludeDirective: nonTlbTranscludeDirective
18109 ii = directives.length;
18110 } else if (directive.compile) {
18112 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
18113 if (isFunction(linkFn)) {
18114 addLinkFns(null, linkFn, attrStart, attrEnd);
18115 } else if (linkFn) {
18116 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
18119 $exceptionHandler(e, startingTag($compileNode));
18123 if (directive.terminal) {
18124 nodeLinkFn.terminal = true;
18125 terminalPriority = Math.max(terminalPriority, directive.priority);
18130 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
18131 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
18132 nodeLinkFn.templateOnThisElement = hasTemplate;
18133 nodeLinkFn.transclude = childTranscludeFn;
18135 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
18137 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
18140 ////////////////////
18142 function addLinkFns(pre, post, attrStart, attrEnd) {
18144 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
18145 pre.require = directive.require;
18146 pre.directiveName = directiveName;
18147 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
18148 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
18150 preLinkFns.push(pre);
18153 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
18154 post.require = directive.require;
18155 post.directiveName = directiveName;
18156 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
18157 post = cloneAndAnnotateFn(post, {isolateScope: true});
18159 postLinkFns.push(post);
18163 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
18164 var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
18165 attrs, scopeBindingInfo;
18167 if (compileNode === linkNode) {
18168 attrs = templateAttrs;
18169 $element = templateAttrs.$$element;
18171 $element = jqLite(linkNode);
18172 attrs = new Attributes($element, templateAttrs);
18175 controllerScope = scope;
18176 if (newIsolateScopeDirective) {
18177 isolateScope = scope.$new(true);
18178 } else if (newScopeDirective) {
18179 controllerScope = scope.$parent;
18182 if (boundTranscludeFn) {
18183 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
18184 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
18185 transcludeFn = controllersBoundTransclude;
18186 transcludeFn.$$boundTransclude = boundTranscludeFn;
18187 // expose the slots on the `$transclude` function
18188 transcludeFn.isSlotFilled = function(slotName) {
18189 return !!boundTranscludeFn.$$slots[slotName];
18193 if (controllerDirectives) {
18194 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
18197 if (newIsolateScopeDirective) {
18198 // Initialize isolate scope bindings for new isolate scope directive.
18199 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
18200 templateDirective === newIsolateScopeDirective.$$originalDirective)));
18201 compile.$$addScopeClass($element, true);
18202 isolateScope.$$isolateBindings =
18203 newIsolateScopeDirective.$$isolateBindings;
18204 scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
18205 isolateScope.$$isolateBindings,
18206 newIsolateScopeDirective);
18207 if (scopeBindingInfo.removeWatches) {
18208 isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
18212 // Initialize bindToController bindings
18213 for (var name in elementControllers) {
18214 var controllerDirective = controllerDirectives[name];
18215 var controller = elementControllers[name];
18216 var bindings = controllerDirective.$$bindings.bindToController;
18218 if (controller.identifier && bindings) {
18219 controller.bindingInfo =
18220 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
18222 controller.bindingInfo = {};
18225 var controllerResult = controller();
18226 if (controllerResult !== controller.instance) {
18227 // If the controller constructor has a return value, overwrite the instance
18228 // from setupControllers
18229 controller.instance = controllerResult;
18230 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
18231 controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
18232 controller.bindingInfo =
18233 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
18237 // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
18238 forEach(controllerDirectives, function(controllerDirective, name) {
18239 var require = controllerDirective.require;
18240 if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
18241 extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
18245 // Handle the init and destroy lifecycle hooks on all controllers that have them
18246 forEach(elementControllers, function(controller) {
18247 var controllerInstance = controller.instance;
18248 if (isFunction(controllerInstance.$onChanges)) {
18249 controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
18251 if (isFunction(controllerInstance.$onInit)) {
18252 controllerInstance.$onInit();
18254 if (isFunction(controllerInstance.$onDestroy)) {
18255 controllerScope.$on('$destroy', function callOnDestroyHook() {
18256 controllerInstance.$onDestroy();
18262 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
18263 linkFn = preLinkFns[i];
18264 invokeLinkFn(linkFn,
18265 linkFn.isolateScope ? isolateScope : scope,
18268 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
18274 // We only pass the isolate scope, if the isolate directive has a template,
18275 // otherwise the child elements do not belong to the isolate directive.
18276 var scopeToChild = scope;
18277 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
18278 scopeToChild = isolateScope;
18280 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
18283 for (i = postLinkFns.length - 1; i >= 0; i--) {
18284 linkFn = postLinkFns[i];
18285 invokeLinkFn(linkFn,
18286 linkFn.isolateScope ? isolateScope : scope,
18289 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
18294 // Trigger $postLink lifecycle hooks
18295 forEach(elementControllers, function(controller) {
18296 var controllerInstance = controller.instance;
18297 if (isFunction(controllerInstance.$postLink)) {
18298 controllerInstance.$postLink();
18302 // This is the function that is injected as `$transclude`.
18303 // Note: all arguments are optional!
18304 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
18305 var transcludeControllers;
18306 // No scope passed in:
18307 if (!isScope(scope)) {
18308 slotName = futureParentElement;
18309 futureParentElement = cloneAttachFn;
18310 cloneAttachFn = scope;
18314 if (hasElementTranscludeDirective) {
18315 transcludeControllers = elementControllers;
18317 if (!futureParentElement) {
18318 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
18321 // slotTranscludeFn can be one of three things:
18322 // * a transclude function - a filled slot
18323 // * `null` - an optional slot that was not filled
18324 // * `undefined` - a slot that was not declared (i.e. invalid)
18325 var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
18326 if (slotTranscludeFn) {
18327 return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
18328 } else if (isUndefined(slotTranscludeFn)) {
18329 throw $compileMinErr('noslot',
18330 'No parent directive that requires a transclusion with slot name "{0}". ' +
18332 slotName, startingTag($element));
18335 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
18341 function getControllers(directiveName, require, $element, elementControllers) {
18344 if (isString(require)) {
18345 var match = require.match(REQUIRE_PREFIX_REGEXP);
18346 var name = require.substring(match[0].length);
18347 var inheritType = match[1] || match[3];
18348 var optional = match[2] === '?';
18350 //If only parents then start at the parent element
18351 if (inheritType === '^^') {
18352 $element = $element.parent();
18353 //Otherwise attempt getting the controller from elementControllers in case
18354 //the element is transcluded (and has no data) and to avoid .data if possible
18356 value = elementControllers && elementControllers[name];
18357 value = value && value.instance;
18361 var dataName = '$' + name + 'Controller';
18362 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
18365 if (!value && !optional) {
18366 throw $compileMinErr('ctreq',
18367 "Controller '{0}', required by directive '{1}', can't be found!",
18368 name, directiveName);
18370 } else if (isArray(require)) {
18372 for (var i = 0, ii = require.length; i < ii; i++) {
18373 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
18375 } else if (isObject(require)) {
18377 forEach(require, function(controller, property) {
18378 value[property] = getControllers(directiveName, controller, $element, elementControllers);
18382 return value || null;
18385 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
18386 var elementControllers = createMap();
18387 for (var controllerKey in controllerDirectives) {
18388 var directive = controllerDirectives[controllerKey];
18390 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
18391 $element: $element,
18393 $transclude: transcludeFn
18396 var controller = directive.controller;
18397 if (controller == '@') {
18398 controller = attrs[directive.name];
18401 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
18403 // For directives with element transclusion the element is a comment.
18404 // In this case .data will not attach any data.
18405 // Instead, we save the controllers for the element in a local hash and attach to .data
18406 // later, once we have the actual element.
18407 elementControllers[directive.name] = controllerInstance;
18408 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
18410 return elementControllers;
18413 // Depending upon the context in which a directive finds itself it might need to have a new isolated
18414 // or child scope created. For instance:
18415 // * if the directive has been pulled into a template because another directive with a higher priority
18416 // asked for element transclusion
18417 // * if the directive itself asks for transclusion but it is at the root of a template and the original
18418 // element was replaced. See https://github.com/angular/angular.js/issues/12936
18419 function markDirectiveScope(directives, isolateScope, newScope) {
18420 for (var j = 0, jj = directives.length; j < jj; j++) {
18421 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
18426 * looks up the directive and decorates it with exception handling and proper parameters. We
18427 * call this the boundDirective.
18429 * @param {string} name name of the directive to look up.
18430 * @param {string} location The directive must be found in specific format.
18431 * String containing any of theses characters:
18433 * * `E`: element name
18437 * @returns {boolean} true if directive was added.
18439 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
18441 if (name === ignoreDirective) return null;
18443 if (hasDirectives.hasOwnProperty(name)) {
18444 for (var directive, directives = $injector.get(name + Suffix),
18445 i = 0, ii = directives.length; i < ii; i++) {
18447 directive = directives[i];
18448 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
18449 directive.restrict.indexOf(location) != -1) {
18450 if (startAttrName) {
18451 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
18453 if (!directive.$$bindings) {
18454 var bindings = directive.$$bindings =
18455 parseDirectiveBindings(directive, directive.name);
18456 if (isObject(bindings.isolateScope)) {
18457 directive.$$isolateBindings = bindings.isolateScope;
18460 tDirectives.push(directive);
18463 } catch (e) { $exceptionHandler(e); }
18471 * looks up the directive and returns true if it is a multi-element directive,
18472 * and therefore requires DOM nodes between -start and -end markers to be grouped
18475 * @param {string} name name of the directive to look up.
18476 * @returns true if directive was registered as multi-element.
18478 function directiveIsMultiElement(name) {
18479 if (hasDirectives.hasOwnProperty(name)) {
18480 for (var directive, directives = $injector.get(name + Suffix),
18481 i = 0, ii = directives.length; i < ii; i++) {
18482 directive = directives[i];
18483 if (directive.multiElement) {
18492 * When the element is replaced with HTML template then the new attributes
18493 * on the template need to be merged with the existing attributes in the DOM.
18494 * The desired effect is to have both of the attributes present.
18496 * @param {object} dst destination attributes (original DOM)
18497 * @param {object} src source attributes (from the directive template)
18499 function mergeTemplateAttributes(dst, src) {
18500 var srcAttr = src.$attr,
18501 dstAttr = dst.$attr,
18502 $element = dst.$$element;
18504 // reapply the old attributes to the new element
18505 forEach(dst, function(value, key) {
18506 if (key.charAt(0) != '$') {
18507 if (src[key] && src[key] !== value) {
18508 value += (key === 'style' ? ';' : ' ') + src[key];
18510 dst.$set(key, value, true, srcAttr[key]);
18514 // copy the new attributes on the old attrs object
18515 forEach(src, function(value, key) {
18516 if (key == 'class') {
18517 safeAddClass($element, value);
18518 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
18519 } else if (key == 'style') {
18520 $element.attr('style', $element.attr('style') + ';' + value);
18521 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
18522 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
18523 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
18524 // have an attribute like "has-own-property" or "data-has-own-property", etc.
18525 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
18527 dstAttr[key] = srcAttr[key];
18533 function compileTemplateUrl(directives, $compileNode, tAttrs,
18534 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
18535 var linkQueue = [],
18536 afterTemplateNodeLinkFn,
18537 afterTemplateChildLinkFn,
18538 beforeTemplateCompileNode = $compileNode[0],
18539 origAsyncDirective = directives.shift(),
18540 derivedSyncDirective = inherit(origAsyncDirective, {
18541 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
18543 templateUrl = (isFunction(origAsyncDirective.templateUrl))
18544 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
18545 : origAsyncDirective.templateUrl,
18546 templateNamespace = origAsyncDirective.templateNamespace;
18548 $compileNode.empty();
18550 $templateRequest(templateUrl)
18551 .then(function(content) {
18552 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
18554 content = denormalizeTemplate(content);
18556 if (origAsyncDirective.replace) {
18557 if (jqLiteIsTextNode(content)) {
18560 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
18562 compileNode = $template[0];
18564 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
18565 throw $compileMinErr('tplrt',
18566 "Template for directive '{0}' must have exactly one root element. {1}",
18567 origAsyncDirective.name, templateUrl);
18570 tempTemplateAttrs = {$attr: {}};
18571 replaceWith($rootElement, $compileNode, compileNode);
18572 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
18574 if (isObject(origAsyncDirective.scope)) {
18575 // the original directive that caused the template to be loaded async required
18576 // an isolate scope
18577 markDirectiveScope(templateDirectives, true);
18579 directives = templateDirectives.concat(directives);
18580 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
18582 compileNode = beforeTemplateCompileNode;
18583 $compileNode.html(content);
18586 directives.unshift(derivedSyncDirective);
18588 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
18589 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
18590 previousCompileContext);
18591 forEach($rootElement, function(node, i) {
18592 if (node == compileNode) {
18593 $rootElement[i] = $compileNode[0];
18596 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
18598 while (linkQueue.length) {
18599 var scope = linkQueue.shift(),
18600 beforeTemplateLinkNode = linkQueue.shift(),
18601 linkRootElement = linkQueue.shift(),
18602 boundTranscludeFn = linkQueue.shift(),
18603 linkNode = $compileNode[0];
18605 if (scope.$$destroyed) continue;
18607 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
18608 var oldClasses = beforeTemplateLinkNode.className;
18610 if (!(previousCompileContext.hasElementTranscludeDirective &&
18611 origAsyncDirective.replace)) {
18612 // it was cloned therefore we have to clone as well.
18613 linkNode = jqLiteClone(compileNode);
18615 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
18617 // Copy in CSS classes from original node
18618 safeAddClass(jqLite(linkNode), oldClasses);
18620 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
18621 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
18623 childBoundTranscludeFn = boundTranscludeFn;
18625 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
18626 childBoundTranscludeFn);
18631 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
18632 var childBoundTranscludeFn = boundTranscludeFn;
18633 if (scope.$$destroyed) return;
18635 linkQueue.push(scope,
18638 childBoundTranscludeFn);
18640 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
18641 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
18643 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
18650 * Sorting function for bound directives.
18652 function byPriority(a, b) {
18653 var diff = b.priority - a.priority;
18654 if (diff !== 0) return diff;
18655 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
18656 return a.index - b.index;
18659 function assertNoDuplicate(what, previousDirective, directive, element) {
18661 function wrapModuleNameIfDefined(moduleName) {
18662 return moduleName ?
18663 (' (module: ' + moduleName + ')') :
18667 if (previousDirective) {
18668 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
18669 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
18670 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
18675 function addTextInterpolateDirective(directives, text) {
18676 var interpolateFn = $interpolate(text, true);
18677 if (interpolateFn) {
18680 compile: function textInterpolateCompileFn(templateNode) {
18681 var templateNodeParent = templateNode.parent(),
18682 hasCompileParent = !!templateNodeParent.length;
18684 // When transcluding a template that has bindings in the root
18685 // we don't have a parent and thus need to add the class during linking fn.
18686 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
18688 return function textInterpolateLinkFn(scope, node) {
18689 var parent = node.parent();
18690 if (!hasCompileParent) compile.$$addBindingClass(parent);
18691 compile.$$addBindingInfo(parent, interpolateFn.expressions);
18692 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
18693 node[0].nodeValue = value;
18702 function wrapTemplate(type, template) {
18703 type = lowercase(type || 'html');
18707 var wrapper = window.document.createElement('div');
18708 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
18709 return wrapper.childNodes[0].childNodes;
18716 function getTrustedContext(node, attrNormalizedName) {
18717 if (attrNormalizedName == "srcdoc") {
18720 var tag = nodeName_(node);
18721 // maction[xlink:href] can source SVG. It's not limited to <maction>.
18722 if (attrNormalizedName == "xlinkHref" ||
18723 (tag == "form" && attrNormalizedName == "action") ||
18724 (tag != "img" && (attrNormalizedName == "src" ||
18725 attrNormalizedName == "ngSrc"))) {
18726 return $sce.RESOURCE_URL;
18731 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
18732 var trustedContext = getTrustedContext(node, name);
18733 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
18735 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
18737 // no interpolation found -> ignore
18738 if (!interpolateFn) return;
18741 if (name === "multiple" && nodeName_(node) === "select") {
18742 throw $compileMinErr("selmulti",
18743 "Binding to the 'multiple' attribute is not supported. Element: {0}",
18744 startingTag(node));
18749 compile: function() {
18751 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
18752 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
18754 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
18755 throw $compileMinErr('nodomevents',
18756 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
18757 "ng- versions (such as ng-click instead of onclick) instead.");
18760 // If the attribute has changed since last $interpolate()ed
18761 var newValue = attr[name];
18762 if (newValue !== value) {
18763 // we need to interpolate again since the attribute value has been updated
18764 // (e.g. by another directive's compile function)
18765 // ensure unset/empty values make interpolateFn falsy
18766 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
18770 // if attribute was updated so that there is no interpolation going on we don't want to
18771 // register any observers
18772 if (!interpolateFn) return;
18774 // initialize attr object so that it's ready in case we need the value for isolate
18775 // scope initialization, otherwise the value would not be available from isolate
18776 // directive's linking fn during linking phase
18777 attr[name] = interpolateFn(scope);
18779 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
18780 (attr.$$observers && attr.$$observers[name].$$scope || scope).
18781 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
18782 //special case for class attribute addition + removal
18783 //so that class changes can tap into the animation
18784 //hooks provided by the $animate service. Be sure to
18785 //skip animations when the first digest occurs (when
18786 //both the new and the old values are the same) since
18787 //the CSS classes are the non-interpolated values
18788 if (name === 'class' && newValue != oldValue) {
18789 attr.$updateClass(newValue, oldValue);
18791 attr.$set(name, newValue);
18802 * This is a special jqLite.replaceWith, which can replace items which
18803 * have no parents, provided that the containing jqLite collection is provided.
18805 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
18806 * in the root of the tree.
18807 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
18808 * the shell, but replace its DOM node reference.
18809 * @param {Node} newNode The new DOM node.
18811 function replaceWith($rootElement, elementsToRemove, newNode) {
18812 var firstElementToRemove = elementsToRemove[0],
18813 removeCount = elementsToRemove.length,
18814 parent = firstElementToRemove.parentNode,
18817 if ($rootElement) {
18818 for (i = 0, ii = $rootElement.length; i < ii; i++) {
18819 if ($rootElement[i] == firstElementToRemove) {
18820 $rootElement[i++] = newNode;
18821 for (var j = i, j2 = j + removeCount - 1,
18822 jj = $rootElement.length;
18823 j < jj; j++, j2++) {
18825 $rootElement[j] = $rootElement[j2];
18827 delete $rootElement[j];
18830 $rootElement.length -= removeCount - 1;
18832 // If the replaced element is also the jQuery .context then replace it
18833 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
18834 // http://api.jquery.com/context/
18835 if ($rootElement.context === firstElementToRemove) {
18836 $rootElement.context = newNode;
18844 parent.replaceChild(newNode, firstElementToRemove);
18847 // Append all the `elementsToRemove` to a fragment. This will...
18848 // - remove them from the DOM
18849 // - allow them to still be traversed with .nextSibling
18850 // - allow a single fragment.qSA to fetch all elements being removed
18851 var fragment = window.document.createDocumentFragment();
18852 for (i = 0; i < removeCount; i++) {
18853 fragment.appendChild(elementsToRemove[i]);
18856 if (jqLite.hasData(firstElementToRemove)) {
18857 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
18858 // data here because there's no public interface in jQuery to do that and copying over
18859 // event listeners (which is the main use of private data) wouldn't work anyway.
18860 jqLite.data(newNode, jqLite.data(firstElementToRemove));
18862 // Remove $destroy event listeners from `firstElementToRemove`
18863 jqLite(firstElementToRemove).off('$destroy');
18866 // Cleanup any data/listeners on the elements and children.
18867 // This includes invoking the $destroy event on any elements with listeners.
18868 jqLite.cleanData(fragment.querySelectorAll('*'));
18870 // Update the jqLite collection to only contain the `newNode`
18871 for (i = 1; i < removeCount; i++) {
18872 delete elementsToRemove[i];
18874 elementsToRemove[0] = newNode;
18875 elementsToRemove.length = 1;
18879 function cloneAndAnnotateFn(fn, annotation) {
18880 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
18884 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
18886 linkFn(scope, $element, attrs, controllers, transcludeFn);
18888 $exceptionHandler(e, startingTag($element));
18893 // Set up $watches for isolate scope and controller bindings. This process
18894 // only occurs for isolate scopes and new scopes with controllerAs.
18895 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
18896 var removeWatchCollection = [];
18897 var initialChanges = {};
18899 forEach(bindings, function initializeBinding(definition, scopeName) {
18900 var attrName = definition.attrName,
18901 optional = definition.optional,
18902 mode = definition.mode, // @, =, or &
18904 parentGet, parentSet, compare, removeWatch;
18909 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
18910 destination[scopeName] = attrs[attrName] = void 0;
18912 attrs.$observe(attrName, function(value) {
18913 if (isString(value) || isBoolean(value)) {
18914 var oldValue = destination[scopeName];
18915 recordChanges(scopeName, value, oldValue);
18916 destination[scopeName] = value;
18919 attrs.$$observers[attrName].$$scope = scope;
18920 lastValue = attrs[attrName];
18921 if (isString(lastValue)) {
18922 // If the attribute has been provided then we trigger an interpolation to ensure
18923 // the value is there for use in the link fn
18924 destination[scopeName] = $interpolate(lastValue)(scope);
18925 } else if (isBoolean(lastValue)) {
18926 // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
18927 // the value to boolean rather than a string, so we special case this situation
18928 destination[scopeName] = lastValue;
18930 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
18934 if (!hasOwnProperty.call(attrs, attrName)) {
18935 if (optional) break;
18936 attrs[attrName] = void 0;
18938 if (optional && !attrs[attrName]) break;
18940 parentGet = $parse(attrs[attrName]);
18941 if (parentGet.literal) {
18944 compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
18946 parentSet = parentGet.assign || function() {
18947 // reset the change, or we will throw this exception on every $digest
18948 lastValue = destination[scopeName] = parentGet(scope);
18949 throw $compileMinErr('nonassign',
18950 "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
18951 attrs[attrName], attrName, directive.name);
18953 lastValue = destination[scopeName] = parentGet(scope);
18954 var parentValueWatch = function parentValueWatch(parentValue) {
18955 if (!compare(parentValue, destination[scopeName])) {
18956 // we are out of sync and need to copy
18957 if (!compare(parentValue, lastValue)) {
18958 // parent changed and it has precedence
18959 destination[scopeName] = parentValue;
18961 // if the parent can be assigned then do so
18962 parentSet(scope, parentValue = destination[scopeName]);
18965 return lastValue = parentValue;
18967 parentValueWatch.$stateful = true;
18968 if (definition.collection) {
18969 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
18971 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
18973 removeWatchCollection.push(removeWatch);
18977 if (!hasOwnProperty.call(attrs, attrName)) {
18978 if (optional) break;
18979 attrs[attrName] = void 0;
18981 if (optional && !attrs[attrName]) break;
18983 parentGet = $parse(attrs[attrName]);
18985 destination[scopeName] = parentGet(scope);
18986 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
18988 removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
18989 if (newValue === oldValue) {
18990 // If the new and old values are identical then this is the first time the watch has been triggered
18991 // So instead we use the current value on the destination as the old value
18992 oldValue = destination[scopeName];
18994 recordChanges(scopeName, newValue, oldValue);
18995 destination[scopeName] = newValue;
18996 }, parentGet.literal);
18998 removeWatchCollection.push(removeWatch);
19002 // Don't assign Object.prototype method to scope
19003 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
19005 // Don't assign noop to destination if expression is not valid
19006 if (parentGet === noop && optional) break;
19008 destination[scopeName] = function(locals) {
19009 return parentGet(scope, locals);
19015 function recordChanges(key, currentValue, previousValue) {
19016 if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
19017 // If we have not already scheduled the top level onChangesQueue handler then do so now
19018 if (!onChangesQueue) {
19019 scope.$$postDigest(flushOnChangesQueue);
19020 onChangesQueue = [];
19022 // If we have not already queued a trigger of onChanges for this controller then do so now
19025 onChangesQueue.push(triggerOnChangesHook);
19027 // If the has been a change on this property already then we need to reuse the previous value
19028 if (changes[key]) {
19029 previousValue = changes[key].previousValue;
19031 // Store this change
19032 changes[key] = new SimpleChange(previousValue, currentValue);
19036 function triggerOnChangesHook() {
19037 destination.$onChanges(changes);
19038 // Now clear the changes so that we schedule onChanges when more changes arrive
19039 changes = undefined;
19043 initialChanges: initialChanges,
19044 removeWatches: removeWatchCollection.length && function removeWatches() {
19045 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
19046 removeWatchCollection[i]();
19054 function SimpleChange(previous, current) {
19055 this.previousValue = previous;
19056 this.currentValue = current;
19058 SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
19061 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
19063 * Converts all accepted directives format into proper directive name.
19064 * @param name Name to normalize
19066 function directiveNormalize(name) {
19067 return camelCase(name.replace(PREFIX_REGEXP, ''));
19072 * @name $compile.directive.Attributes
19075 * A shared object between directive compile / linking functions which contains normalized DOM
19076 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
19077 * needed since all of these are treated as equivalent in Angular:
19080 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
19086 * @name $compile.directive.Attributes#$attr
19089 * A map of DOM element attribute names to the normalized name. This is
19090 * needed to do reverse lookup from normalized name back to actual name.
19096 * @name $compile.directive.Attributes#$set
19100 * Set DOM element attribute value.
19103 * @param {string} name Normalized element attribute name of the property to modify. The name is
19104 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
19105 * property to the original name.
19106 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
19112 * Closure compiler type information
19115 function nodesetLinkingFn(
19116 /* angular.Scope */ scope,
19117 /* NodeList */ nodeList,
19118 /* Element */ rootElement,
19119 /* function(Function) */ boundTranscludeFn
19122 function directiveLinkingFn(
19123 /* nodesetLinkingFn */ nodesetLinkingFn,
19124 /* angular.Scope */ scope,
19126 /* Element */ rootElement,
19127 /* function(Function) */ boundTranscludeFn
19130 function tokenDifference(str1, str2) {
19132 tokens1 = str1.split(/\s+/),
19133 tokens2 = str2.split(/\s+/);
19136 for (var i = 0; i < tokens1.length; i++) {
19137 var token = tokens1[i];
19138 for (var j = 0; j < tokens2.length; j++) {
19139 if (token == tokens2[j]) continue outer;
19141 values += (values.length > 0 ? ' ' : '') + token;
19146 function removeComments(jqNodes) {
19147 jqNodes = jqLite(jqNodes);
19148 var i = jqNodes.length;
19155 var node = jqNodes[i];
19156 if (node.nodeType === NODE_TYPE_COMMENT) {
19157 splice.call(jqNodes, i, 1);
19163 var $controllerMinErr = minErr('$controller');
19166 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
19167 function identifierForController(controller, ident) {
19168 if (ident && isString(ident)) return ident;
19169 if (isString(controller)) {
19170 var match = CNTRL_REG.exec(controller);
19171 if (match) return match[3];
19178 * @name $controllerProvider
19180 * The {@link ng.$controller $controller service} is used by Angular to create new
19183 * This provider allows controller registration via the
19184 * {@link ng.$controllerProvider#register register} method.
19186 function $ControllerProvider() {
19187 var controllers = {},
19192 * @name $controllerProvider#has
19193 * @param {string} name Controller name to check.
19195 this.has = function(name) {
19196 return controllers.hasOwnProperty(name);
19201 * @name $controllerProvider#register
19202 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
19203 * the names and the values are the constructors.
19204 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
19205 * annotations in the array notation).
19207 this.register = function(name, constructor) {
19208 assertNotHasOwnProperty(name, 'controller');
19209 if (isObject(name)) {
19210 extend(controllers, name);
19212 controllers[name] = constructor;
19218 * @name $controllerProvider#allowGlobals
19219 * @description If called, allows `$controller` to find controller constructors on `window`
19221 this.allowGlobals = function() {
19226 this.$get = ['$injector', '$window', function($injector, $window) {
19230 * @name $controller
19231 * @requires $injector
19233 * @param {Function|string} constructor If called with a function then it's considered to be the
19234 * controller constructor function. Otherwise it's considered to be a string which is used
19235 * to retrieve the controller constructor using the following steps:
19237 * * check if a controller with given name is registered via `$controllerProvider`
19238 * * check if evaluating the string on the current scope returns a constructor
19239 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
19240 * `window` object (not recommended)
19242 * The string can use the `controller as property` syntax, where the controller instance is published
19243 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
19244 * to work correctly.
19246 * @param {Object} locals Injection locals for Controller.
19247 * @return {Object} Instance of given controller.
19250 * `$controller` service is responsible for instantiating controllers.
19252 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
19253 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
19255 return function $controller(expression, locals, later, ident) {
19257 // param `later` --- indicates that the controller's constructor is invoked at a later time.
19258 // If true, $controller will allocate the object with the correct
19259 // prototype chain, but will not invoke the controller until a returned
19260 // callback is invoked.
19261 // param `ident` --- An optional label which overrides the label parsed from the controller
19262 // expression, if any.
19263 var instance, match, constructor, identifier;
19264 later = later === true;
19265 if (ident && isString(ident)) {
19266 identifier = ident;
19269 if (isString(expression)) {
19270 match = expression.match(CNTRL_REG);
19272 throw $controllerMinErr('ctrlfmt',
19273 "Badly formed controller string '{0}'. " +
19274 "Must match `__name__ as __id__` or `__name__`.", expression);
19276 constructor = match[1],
19277 identifier = identifier || match[3];
19278 expression = controllers.hasOwnProperty(constructor)
19279 ? controllers[constructor]
19280 : getter(locals.$scope, constructor, true) ||
19281 (globals ? getter($window, constructor, true) : undefined);
19283 assertArgFn(expression, constructor, true);
19287 // Instantiate controller later:
19288 // This machinery is used to create an instance of the object before calling the
19289 // controller's constructor itself.
19291 // This allows properties to be added to the controller before the constructor is
19292 // invoked. Primarily, this is used for isolate scope bindings in $compile.
19294 // This feature is not intended for use by applications, and is thus not documented
19296 // Object creation: http://jsperf.com/create-constructor/2
19297 var controllerPrototype = (isArray(expression) ?
19298 expression[expression.length - 1] : expression).prototype;
19299 instance = Object.create(controllerPrototype || null);
19302 addIdentifier(locals, identifier, instance, constructor || expression.name);
19306 return instantiate = extend(function $controllerInit() {
19307 var result = $injector.invoke(expression, instance, locals, constructor);
19308 if (result !== instance && (isObject(result) || isFunction(result))) {
19311 // If result changed, re-assign controllerAs value to scope.
19312 addIdentifier(locals, identifier, instance, constructor || expression.name);
19317 instance: instance,
19318 identifier: identifier
19322 instance = $injector.instantiate(expression, locals, constructor);
19325 addIdentifier(locals, identifier, instance, constructor || expression.name);
19331 function addIdentifier(locals, identifier, instance, name) {
19332 if (!(locals && isObject(locals.$scope))) {
19333 throw minErr('$controller')('noscp',
19334 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
19338 locals.$scope[identifier] = instance;
19346 * @requires $window
19349 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
19352 <example module="documentExample">
19353 <file name="index.html">
19354 <div ng-controller="ExampleController">
19355 <p>$document title: <b ng-bind="title"></b></p>
19356 <p>window.document title: <b ng-bind="windowTitle"></b></p>
19359 <file name="script.js">
19360 angular.module('documentExample', [])
19361 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
19362 $scope.title = $document[0].title;
19363 $scope.windowTitle = angular.element(window.document)[0].title;
19368 function $DocumentProvider() {
19369 this.$get = ['$window', function(window) {
19370 return jqLite(window.document);
19376 * @name $exceptionHandler
19377 * @requires ng.$log
19380 * Any uncaught exception in angular expressions is delegated to this service.
19381 * The default implementation simply delegates to `$log.error` which logs it into
19382 * the browser console.
19384 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
19385 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
19390 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
19391 * return function(exception, cause) {
19392 * exception.message += ' (caused by "' + cause + '")';
19398 * This example will override the normal action of `$exceptionHandler`, to make angular
19399 * exceptions fail hard when they happen, instead of just logging to the console.
19402 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
19403 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
19404 * (unless executed during a digest).
19406 * If you wish, you can manually delegate exceptions, e.g.
19407 * `try { ... } catch(e) { $exceptionHandler(e); }`
19409 * @param {Error} exception Exception associated with the error.
19410 * @param {string=} cause optional information about the context in which
19411 * the error was thrown.
19414 function $ExceptionHandlerProvider() {
19415 this.$get = ['$log', function($log) {
19416 return function(exception, cause) {
19417 $log.error.apply($log, arguments);
19422 var $$ForceReflowProvider = function() {
19423 this.$get = ['$document', function($document) {
19424 return function(domNode) {
19425 //the line below will force the browser to perform a repaint so
19426 //that all the animated elements within the animation frame will
19427 //be properly updated and drawn on screen. This is required to
19428 //ensure that the preparation animation is properly flushed so that
19429 //the active state picks up from there. DO NOT REMOVE THIS LINE.
19430 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
19431 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
19432 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
19434 if (!domNode.nodeType && domNode instanceof jqLite) {
19435 domNode = domNode[0];
19438 domNode = $document[0].body;
19440 return domNode.offsetWidth + 1;
19445 var APPLICATION_JSON = 'application/json';
19446 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
19447 var JSON_START = /^\[|^\{(?!\{)/;
19452 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
19453 var $httpMinErr = minErr('$http');
19454 var $httpMinErrLegacyFn = function(method) {
19455 return function() {
19456 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
19460 function serializeValue(v) {
19462 return isDate(v) ? v.toISOString() : toJson(v);
19468 function $HttpParamSerializerProvider() {
19471 * @name $httpParamSerializer
19474 * Default {@link $http `$http`} params serializer that converts objects to strings
19475 * according to the following rules:
19477 * * `{'foo': 'bar'}` results in `foo=bar`
19478 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
19479 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
19480 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
19482 * Note that serializer will sort the request parameters alphabetically.
19485 this.$get = function() {
19486 return function ngParamSerializer(params) {
19487 if (!params) return '';
19489 forEachSorted(params, function(value, key) {
19490 if (value === null || isUndefined(value)) return;
19491 if (isArray(value)) {
19492 forEach(value, function(v) {
19493 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
19496 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
19500 return parts.join('&');
19505 function $HttpParamSerializerJQLikeProvider() {
19508 * @name $httpParamSerializerJQLike
19511 * Alternative {@link $http `$http`} params serializer that follows
19512 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
19513 * The serializer will also sort the params alphabetically.
19515 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
19521 * params: myParams,
19522 * paramSerializer: '$httpParamSerializerJQLike'
19526 * It is also possible to set it as the default `paramSerializer` in the
19527 * {@link $httpProvider#defaults `$httpProvider`}.
19529 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
19530 * form data for submission:
19533 * .controller(function($http, $httpParamSerializerJQLike) {
19539 * data: $httpParamSerializerJQLike(myData),
19541 * 'Content-Type': 'application/x-www-form-urlencoded'
19549 this.$get = function() {
19550 return function jQueryLikeParamSerializer(params) {
19551 if (!params) return '';
19553 serialize(params, '', true);
19554 return parts.join('&');
19556 function serialize(toSerialize, prefix, topLevel) {
19557 if (toSerialize === null || isUndefined(toSerialize)) return;
19558 if (isArray(toSerialize)) {
19559 forEach(toSerialize, function(value, index) {
19560 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
19562 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
19563 forEachSorted(toSerialize, function(value, key) {
19564 serialize(value, prefix +
19565 (topLevel ? '' : '[') +
19567 (topLevel ? '' : ']'));
19570 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
19577 function defaultHttpResponseTransform(data, headers) {
19578 if (isString(data)) {
19579 // Strip json vulnerability protection prefix and trim whitespace
19580 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
19583 var contentType = headers('Content-Type');
19584 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
19585 data = fromJson(tempData);
19593 function isJsonLike(str) {
19594 var jsonStart = str.match(JSON_START);
19595 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
19599 * Parse headers into key value object
19601 * @param {string} headers Raw headers as a string
19602 * @returns {Object} Parsed headers as key value object
19604 function parseHeaders(headers) {
19605 var parsed = createMap(), i;
19607 function fillInParsed(key, val) {
19609 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
19613 if (isString(headers)) {
19614 forEach(headers.split('\n'), function(line) {
19615 i = line.indexOf(':');
19616 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
19618 } else if (isObject(headers)) {
19619 forEach(headers, function(headerVal, headerKey) {
19620 fillInParsed(lowercase(headerKey), trim(headerVal));
19629 * Returns a function that provides access to parsed headers.
19631 * Headers are lazy parsed when first requested.
19632 * @see parseHeaders
19634 * @param {(string|Object)} headers Headers to provide access to.
19635 * @returns {function(string=)} Returns a getter function which if called with:
19637 * - if called with single an argument returns a single header value or null
19638 * - if called with no arguments returns an object containing all headers.
19640 function headersGetter(headers) {
19643 return function(name) {
19644 if (!headersObj) headersObj = parseHeaders(headers);
19647 var value = headersObj[lowercase(name)];
19648 if (value === void 0) {
19660 * Chain all given functions
19662 * This function is used for both request and response transforming
19664 * @param {*} data Data to transform.
19665 * @param {function(string=)} headers HTTP headers getter fn.
19666 * @param {number} status HTTP status code of the response.
19667 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
19668 * @returns {*} Transformed data.
19670 function transformData(data, headers, status, fns) {
19671 if (isFunction(fns)) {
19672 return fns(data, headers, status);
19675 forEach(fns, function(fn) {
19676 data = fn(data, headers, status);
19683 function isSuccess(status) {
19684 return 200 <= status && status < 300;
19690 * @name $httpProvider
19692 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
19694 function $HttpProvider() {
19697 * @name $httpProvider#defaults
19700 * Object containing default values for all {@link ng.$http $http} requests.
19702 * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
19703 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
19704 * by default. See {@link $http#caching $http Caching} for more information.
19706 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
19707 * Defaults value is `'XSRF-TOKEN'`.
19709 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
19710 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
19712 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
19713 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
19714 * setting default headers.
19715 * - **`defaults.headers.common`**
19716 * - **`defaults.headers.post`**
19717 * - **`defaults.headers.put`**
19718 * - **`defaults.headers.patch`**
19721 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
19722 * used to the prepare string representation of request parameters (specified as an object).
19723 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
19724 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
19727 var defaults = this.defaults = {
19728 // transform incoming response data
19729 transformResponse: [defaultHttpResponseTransform],
19731 // transform outgoing request data
19732 transformRequest: [function(d) {
19733 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
19739 'Accept': 'application/json, text/plain, */*'
19741 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
19742 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
19743 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
19746 xsrfCookieName: 'XSRF-TOKEN',
19747 xsrfHeaderName: 'X-XSRF-TOKEN',
19749 paramSerializer: '$httpParamSerializer'
19752 var useApplyAsync = false;
19755 * @name $httpProvider#useApplyAsync
19758 * Configure $http service to combine processing of multiple http responses received at around
19759 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
19760 * significant performance improvement for bigger applications that make many HTTP requests
19761 * concurrently (common during application bootstrap).
19763 * Defaults to false. If no value is specified, returns the current configured value.
19765 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
19766 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
19767 * to load and share the same digest cycle.
19769 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
19770 * otherwise, returns the current configured value.
19772 this.useApplyAsync = function(value) {
19773 if (isDefined(value)) {
19774 useApplyAsync = !!value;
19777 return useApplyAsync;
19780 var useLegacyPromise = true;
19783 * @name $httpProvider#useLegacyPromiseExtensions
19786 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
19787 * This should be used to make sure that applications work without these methods.
19789 * Defaults to true. If no value is specified, returns the current configured value.
19791 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
19793 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
19794 * otherwise, returns the current configured value.
19796 this.useLegacyPromiseExtensions = function(value) {
19797 if (isDefined(value)) {
19798 useLegacyPromise = !!value;
19801 return useLegacyPromise;
19806 * @name $httpProvider#interceptors
19809 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
19810 * pre-processing of request or postprocessing of responses.
19812 * These service factories are ordered by request, i.e. they are applied in the same order as the
19813 * array, on request, but reverse order, on response.
19815 * {@link ng.$http#interceptors Interceptors detailed info}
19817 var interceptorFactories = this.interceptors = [];
19819 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
19820 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
19822 var defaultCache = $cacheFactory('$http');
19825 * Make sure that default param serializer is exposed as a function
19827 defaults.paramSerializer = isString(defaults.paramSerializer) ?
19828 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
19831 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
19832 * The reversal is needed so that we can build up the interception chain around the
19835 var reversedInterceptors = [];
19837 forEach(interceptorFactories, function(interceptorFactory) {
19838 reversedInterceptors.unshift(isString(interceptorFactory)
19839 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
19846 * @requires ng.$httpBackend
19847 * @requires $cacheFactory
19848 * @requires $rootScope
19850 * @requires $injector
19853 * The `$http` service is a core Angular service that facilitates communication with the remote
19854 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
19855 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
19857 * For unit testing applications that use `$http` service, see
19858 * {@link ngMock.$httpBackend $httpBackend mock}.
19860 * For a higher level of abstraction, please check out the {@link ngResource.$resource
19861 * $resource} service.
19863 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
19864 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
19865 * it is important to familiarize yourself with these APIs and the guarantees they provide.
19869 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
19870 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
19873 * // Simple GET request example:
19877 * }).then(function successCallback(response) {
19878 * // this callback will be called asynchronously
19879 * // when the response is available
19880 * }, function errorCallback(response) {
19881 * // called asynchronously if an error occurs
19882 * // or server returns response with an error status.
19886 * The response object has these properties:
19888 * - **data** – `{string|Object}` – The response body transformed with the transform
19890 * - **status** – `{number}` – HTTP status code of the response.
19891 * - **headers** – `{function([headerName])}` – Header getter function.
19892 * - **config** – `{Object}` – The configuration object that was used to generate the request.
19893 * - **statusText** – `{string}` – HTTP status text of the response.
19895 * A response status code between 200 and 299 is considered a success status and
19896 * will result in the success callback being called. Note that if the response is a redirect,
19897 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
19898 * called for such responses.
19901 * ## Shortcut methods
19903 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
19904 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
19908 * $http.get('/someUrl', config).then(successCallback, errorCallback);
19909 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
19912 * Complete list of shortcut methods:
19914 * - {@link ng.$http#get $http.get}
19915 * - {@link ng.$http#head $http.head}
19916 * - {@link ng.$http#post $http.post}
19917 * - {@link ng.$http#put $http.put}
19918 * - {@link ng.$http#delete $http.delete}
19919 * - {@link ng.$http#jsonp $http.jsonp}
19920 * - {@link ng.$http#patch $http.patch}
19923 * ## Writing Unit Tests that use $http
19924 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
19925 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
19926 * request using trained responses.
19929 * $httpBackend.expectGET(...);
19931 * $httpBackend.flush();
19934 * ## Deprecation Notice
19935 * <div class="alert alert-danger">
19936 * The `$http` legacy promise methods `success` and `error` have been deprecated.
19937 * Use the standard `then` method instead.
19938 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
19939 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
19942 * ## Setting HTTP Headers
19944 * The $http service will automatically add certain HTTP headers to all requests. These defaults
19945 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
19946 * object, which currently contains this default configuration:
19948 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
19949 * - `Accept: application/json, text/plain, * / *`
19950 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
19951 * - `Content-Type: application/json`
19952 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
19953 * - `Content-Type: application/json`
19955 * To add or overwrite these defaults, simply add or remove a property from these configuration
19956 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
19957 * with the lowercased HTTP method name as the key, e.g.
19958 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
19960 * The defaults can also be set at runtime via the `$http.defaults` object in the same
19961 * fashion. For example:
19964 * module.run(function($http) {
19965 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
19969 * In addition, you can supply a `headers` property in the config object passed when
19970 * calling `$http(config)`, which overrides the defaults without changing them globally.
19972 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
19973 * Use the `headers` property, setting the desired header to `undefined`. For example:
19978 * url: 'http://example.com',
19980 * 'Content-Type': undefined
19982 * data: { test: 'test' }
19985 * $http(req).then(function(){...}, function(){...});
19988 * ## Transforming Requests and Responses
19990 * Both requests and responses can be transformed using transformation functions: `transformRequest`
19991 * and `transformResponse`. These properties can be a single function that returns
19992 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
19993 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
19995 * <div class="alert alert-warning">
19996 * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
19997 * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
19998 * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
19999 * function will be reflected on the scope and in any templates where the object is data-bound.
20000 * To prevent this, transform functions should have no side-effects.
20001 * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
20004 * ### Default Transformations
20006 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
20007 * `defaults.transformResponse` properties. If a request does not provide its own transformations
20008 * then these will be applied.
20010 * You can augment or replace the default transformations by modifying these properties by adding to or
20011 * replacing the array.
20013 * Angular provides the following default transformations:
20015 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
20017 * - If the `data` property of the request configuration object contains an object, serialize it
20018 * into JSON format.
20020 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
20022 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
20023 * - If JSON response is detected, deserialize it using a JSON parser.
20026 * ### Overriding the Default Transformations Per Request
20028 * If you wish override the request/response transformations only for a single request then provide
20029 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
20032 * Note that if you provide these properties on the config object the default transformations will be
20033 * overwritten. If you wish to augment the default transformations then you must include them in your
20034 * local transformation array.
20036 * The following code demonstrates adding a new response transformation to be run after the default response
20037 * transformations have been run.
20040 * function appendTransform(defaults, transform) {
20042 * // We can't guarantee that the default transformation is an array
20043 * defaults = angular.isArray(defaults) ? defaults : [defaults];
20045 * // Append the new transformation to the defaults
20046 * return defaults.concat(transform);
20052 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
20053 * return doTransform(value);
20061 * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
20062 * set the config.cache value or the default cache value to TRUE or to a cache object (created
20063 * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
20064 * precedence over the default cache value.
20067 * * cache all responses - set the default cache value to TRUE or to a cache object
20068 * * cache a specific response - set config.cache value to TRUE or to a cache object
20070 * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
20071 * then the default `$cacheFactory($http)` object is used.
20073 * The default cache value can be set by updating the
20074 * {@link ng.$http#defaults `$http.defaults.cache`} property or the
20075 * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
20077 * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
20078 * the relevant cache object. The next time the same request is made, the response is returned
20079 * from the cache without sending a request to the server.
20083 * * Only GET and JSONP requests are cached.
20084 * * The cache key is the request URL including search parameters; headers are not considered.
20085 * * Cached responses are returned asynchronously, in the same way as responses from the server.
20086 * * If multiple identical requests are made using the same cache, which is not yet populated,
20087 * one request will be made to the server and remaining requests will return the same response.
20088 * * A cache-control header on the response does not affect if or how responses are cached.
20093 * Before you start creating interceptors, be sure to understand the
20094 * {@link ng.$q $q and deferred/promise APIs}.
20096 * For purposes of global error handling, authentication, or any kind of synchronous or
20097 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
20098 * able to intercept requests before they are handed to the server and
20099 * responses before they are handed over to the application code that
20100 * initiated these requests. The interceptors leverage the {@link ng.$q
20101 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
20103 * The interceptors are service factories that are registered with the `$httpProvider` by
20104 * adding them to the `$httpProvider.interceptors` array. The factory is called and
20105 * injected with dependencies (if specified) and returns the interceptor.
20107 * There are two kinds of interceptors (and two kinds of rejection interceptors):
20109 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
20110 * modify the `config` object or create a new one. The function needs to return the `config`
20111 * object directly, or a promise containing the `config` or a new `config` object.
20112 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
20113 * resolved with a rejection.
20114 * * `response`: interceptors get called with http `response` object. The function is free to
20115 * modify the `response` object or create a new one. The function needs to return the `response`
20116 * object directly, or as a promise containing the `response` or a new `response` object.
20117 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
20118 * resolved with a rejection.
20122 * // register the interceptor as a service
20123 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
20125 * // optional method
20126 * 'request': function(config) {
20127 * // do something on success
20131 * // optional method
20132 * 'requestError': function(rejection) {
20133 * // do something on error
20134 * if (canRecover(rejection)) {
20135 * return responseOrNewPromise
20137 * return $q.reject(rejection);
20142 * // optional method
20143 * 'response': function(response) {
20144 * // do something on success
20148 * // optional method
20149 * 'responseError': function(rejection) {
20150 * // do something on error
20151 * if (canRecover(rejection)) {
20152 * return responseOrNewPromise
20154 * return $q.reject(rejection);
20159 * $httpProvider.interceptors.push('myHttpInterceptor');
20162 * // alternatively, register the interceptor via an anonymous factory
20163 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
20165 * 'request': function(config) {
20169 * 'response': function(response) {
20176 * ## Security Considerations
20178 * When designing web applications, consider security threats from:
20180 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
20181 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
20183 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
20184 * pre-configured with strategies that address these issues, but for this to work backend server
20185 * cooperation is required.
20187 * ### JSON Vulnerability Protection
20189 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
20190 * allows third party website to turn your JSON resource URL into
20191 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
20192 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
20193 * Angular will automatically strip the prefix before processing it as JSON.
20195 * For example if your server needs to return:
20200 * which is vulnerable to attack, your server can return:
20206 * Angular will strip the prefix, before processing the JSON.
20209 * ### Cross Site Request Forgery (XSRF) Protection
20211 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
20212 * which the attacker can trick an authenticated user into unknowingly executing actions on your
20213 * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
20214 * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
20215 * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
20216 * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
20217 * The header will not be set for cross-domain requests.
20219 * To take advantage of this, your server needs to set a token in a JavaScript readable session
20220 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
20221 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
20222 * that only JavaScript running on your domain could have sent the request. The token must be
20223 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
20224 * making up its own tokens). We recommend that the token is a digest of your site's
20225 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
20226 * for added security.
20228 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
20229 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
20230 * or the per-request config object.
20232 * In order to prevent collisions in environments where multiple Angular apps share the
20233 * same domain or subdomain, we recommend that each application uses unique cookie name.
20235 * @param {object} config Object describing the request to be made and how it should be
20236 * processed. The object has following properties:
20238 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
20239 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
20240 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
20241 * with the `paramSerializer` and appended as GET parameters.
20242 * - **data** – `{string|Object}` – Data to be sent as the request message data.
20243 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
20244 * HTTP headers to send to the server. If the return value of a function is null, the
20245 * header will not be sent. Functions accept a config object as an argument.
20246 * - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
20247 * To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
20248 * The handler will be called in the context of a `$apply` block.
20249 * - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
20250 * object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
20251 * The handler will be called in the context of a `$apply` block.
20252 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
20253 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
20254 * - **transformRequest** –
20255 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
20256 * transform function or an array of such functions. The transform function takes the http
20257 * request body and headers and returns its transformed (typically serialized) version.
20258 * See {@link ng.$http#overriding-the-default-transformations-per-request
20259 * Overriding the Default Transformations}
20260 * - **transformResponse** –
20261 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
20262 * transform function or an array of such functions. The transform function takes the http
20263 * response body, headers and status and returns its transformed (typically deserialized) version.
20264 * See {@link ng.$http#overriding-the-default-transformations-per-request
20265 * Overriding the Default Transformations}
20266 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
20267 * prepare the string representation of request parameters (specified as an object).
20268 * If specified as string, it is interpreted as function registered with the
20269 * {@link $injector $injector}, which means you can create your own serializer
20270 * by registering it as a {@link auto.$provide#service service}.
20271 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
20272 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
20273 * - **cache** – `{boolean|Object}` – A boolean value or object created with
20274 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
20275 * See {@link $http#caching $http Caching} for more information.
20276 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
20277 * that should abort the request when resolved.
20278 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
20279 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
20280 * for more information.
20281 * - **responseType** - `{string}` - see
20282 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
20284 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
20285 * when the request succeeds or fails.
20288 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
20289 * requests. This is primarily meant to be used for debugging purposes.
20293 <example module="httpExample">
20294 <file name="index.html">
20295 <div ng-controller="FetchController">
20296 <select ng-model="method" aria-label="Request method">
20297 <option>GET</option>
20298 <option>JSONP</option>
20300 <input type="text" ng-model="url" size="80" aria-label="URL" />
20301 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
20302 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
20303 <button id="samplejsonpbtn"
20304 ng-click="updateModel('JSONP',
20305 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
20308 <button id="invalidjsonpbtn"
20309 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
20312 <pre>http status code: {{status}}</pre>
20313 <pre>http response data: {{data}}</pre>
20316 <file name="script.js">
20317 angular.module('httpExample', [])
20318 .controller('FetchController', ['$scope', '$http', '$templateCache',
20319 function($scope, $http, $templateCache) {
20320 $scope.method = 'GET';
20321 $scope.url = 'http-hello.html';
20323 $scope.fetch = function() {
20324 $scope.code = null;
20325 $scope.response = null;
20327 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
20328 then(function(response) {
20329 $scope.status = response.status;
20330 $scope.data = response.data;
20331 }, function(response) {
20332 $scope.data = response.data || "Request failed";
20333 $scope.status = response.status;
20337 $scope.updateModel = function(method, url) {
20338 $scope.method = method;
20343 <file name="http-hello.html">
20346 <file name="protractor.js" type="protractor">
20347 var status = element(by.binding('status'));
20348 var data = element(by.binding('data'));
20349 var fetchBtn = element(by.id('fetchbtn'));
20350 var sampleGetBtn = element(by.id('samplegetbtn'));
20351 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
20352 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
20354 it('should make an xhr GET request', function() {
20355 sampleGetBtn.click();
20357 expect(status.getText()).toMatch('200');
20358 expect(data.getText()).toMatch(/Hello, \$http!/);
20361 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
20362 // it('should make a JSONP request to angularjs.org', function() {
20363 // sampleJsonpBtn.click();
20364 // fetchBtn.click();
20365 // expect(status.getText()).toMatch('200');
20366 // expect(data.getText()).toMatch(/Super Hero!/);
20369 it('should make JSONP request to invalid URL and invoke the error handler',
20371 invalidJsonpBtn.click();
20373 expect(status.getText()).toMatch('0');
20374 expect(data.getText()).toMatch('Request failed');
20379 function $http(requestConfig) {
20381 if (!isObject(requestConfig)) {
20382 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
20385 if (!isString(requestConfig.url)) {
20386 throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
20389 var config = extend({
20391 transformRequest: defaults.transformRequest,
20392 transformResponse: defaults.transformResponse,
20393 paramSerializer: defaults.paramSerializer
20396 config.headers = mergeHeaders(requestConfig);
20397 config.method = uppercase(config.method);
20398 config.paramSerializer = isString(config.paramSerializer) ?
20399 $injector.get(config.paramSerializer) : config.paramSerializer;
20401 var serverRequest = function(config) {
20402 var headers = config.headers;
20403 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
20405 // strip content-type if data is undefined
20406 if (isUndefined(reqData)) {
20407 forEach(headers, function(value, header) {
20408 if (lowercase(header) === 'content-type') {
20409 delete headers[header];
20414 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
20415 config.withCredentials = defaults.withCredentials;
20419 return sendReq(config, reqData).then(transformResponse, transformResponse);
20422 var chain = [serverRequest, undefined];
20423 var promise = $q.when(config);
20425 // apply interceptors
20426 forEach(reversedInterceptors, function(interceptor) {
20427 if (interceptor.request || interceptor.requestError) {
20428 chain.unshift(interceptor.request, interceptor.requestError);
20430 if (interceptor.response || interceptor.responseError) {
20431 chain.push(interceptor.response, interceptor.responseError);
20435 while (chain.length) {
20436 var thenFn = chain.shift();
20437 var rejectFn = chain.shift();
20439 promise = promise.then(thenFn, rejectFn);
20442 if (useLegacyPromise) {
20443 promise.success = function(fn) {
20444 assertArgFn(fn, 'fn');
20446 promise.then(function(response) {
20447 fn(response.data, response.status, response.headers, config);
20452 promise.error = function(fn) {
20453 assertArgFn(fn, 'fn');
20455 promise.then(null, function(response) {
20456 fn(response.data, response.status, response.headers, config);
20461 promise.success = $httpMinErrLegacyFn('success');
20462 promise.error = $httpMinErrLegacyFn('error');
20467 function transformResponse(response) {
20468 // make a copy since the response must be cacheable
20469 var resp = extend({}, response);
20470 resp.data = transformData(response.data, response.headers, response.status,
20471 config.transformResponse);
20472 return (isSuccess(response.status))
20477 function executeHeaderFns(headers, config) {
20478 var headerContent, processedHeaders = {};
20480 forEach(headers, function(headerFn, header) {
20481 if (isFunction(headerFn)) {
20482 headerContent = headerFn(config);
20483 if (headerContent != null) {
20484 processedHeaders[header] = headerContent;
20487 processedHeaders[header] = headerFn;
20491 return processedHeaders;
20494 function mergeHeaders(config) {
20495 var defHeaders = defaults.headers,
20496 reqHeaders = extend({}, config.headers),
20497 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
20499 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
20501 // using for-in instead of forEach to avoid unnecessary iteration after header has been found
20502 defaultHeadersIteration:
20503 for (defHeaderName in defHeaders) {
20504 lowercaseDefHeaderName = lowercase(defHeaderName);
20506 for (reqHeaderName in reqHeaders) {
20507 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
20508 continue defaultHeadersIteration;
20512 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
20515 // execute if header value is a function for merged headers
20516 return executeHeaderFns(reqHeaders, shallowCopy(config));
20520 $http.pendingRequests = [];
20527 * Shortcut method to perform `GET` request.
20529 * @param {string} url Relative or absolute URL specifying the destination of the request
20530 * @param {Object=} config Optional configuration object
20531 * @returns {HttpPromise} Future object
20536 * @name $http#delete
20539 * Shortcut method to perform `DELETE` request.
20541 * @param {string} url Relative or absolute URL specifying the destination of the request
20542 * @param {Object=} config Optional configuration object
20543 * @returns {HttpPromise} Future object
20551 * Shortcut method to perform `HEAD` request.
20553 * @param {string} url Relative or absolute URL specifying the destination of the request
20554 * @param {Object=} config Optional configuration object
20555 * @returns {HttpPromise} Future object
20560 * @name $http#jsonp
20563 * Shortcut method to perform `JSONP` request.
20565 * @param {string} url Relative or absolute URL specifying the destination of the request.
20566 * The name of the callback should be the string `JSON_CALLBACK`.
20567 * @param {Object=} config Optional configuration object
20568 * @returns {HttpPromise} Future object
20570 createShortMethods('get', 'delete', 'head', 'jsonp');
20577 * Shortcut method to perform `POST` request.
20579 * @param {string} url Relative or absolute URL specifying the destination of the request
20580 * @param {*} data Request content
20581 * @param {Object=} config Optional configuration object
20582 * @returns {HttpPromise} Future object
20590 * Shortcut method to perform `PUT` request.
20592 * @param {string} url Relative or absolute URL specifying the destination of the request
20593 * @param {*} data Request content
20594 * @param {Object=} config Optional configuration object
20595 * @returns {HttpPromise} Future object
20600 * @name $http#patch
20603 * Shortcut method to perform `PATCH` request.
20605 * @param {string} url Relative or absolute URL specifying the destination of the request
20606 * @param {*} data Request content
20607 * @param {Object=} config Optional configuration object
20608 * @returns {HttpPromise} Future object
20610 createShortMethodsWithData('post', 'put', 'patch');
20614 * @name $http#defaults
20617 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
20618 * default headers, withCredentials as well as request and response transformations.
20620 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
20622 $http.defaults = defaults;
20628 function createShortMethods(names) {
20629 forEach(arguments, function(name) {
20630 $http[name] = function(url, config) {
20631 return $http(extend({}, config || {}, {
20640 function createShortMethodsWithData(name) {
20641 forEach(arguments, function(name) {
20642 $http[name] = function(url, data, config) {
20643 return $http(extend({}, config || {}, {
20654 * Makes the request.
20656 * !!! ACCESSES CLOSURE VARS:
20657 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
20659 function sendReq(config, reqData) {
20660 var deferred = $q.defer(),
20661 promise = deferred.promise,
20664 reqHeaders = config.headers,
20665 url = buildUrl(config.url, config.paramSerializer(config.params));
20667 $http.pendingRequests.push(config);
20668 promise.then(removePendingReq, removePendingReq);
20671 if ((config.cache || defaults.cache) && config.cache !== false &&
20672 (config.method === 'GET' || config.method === 'JSONP')) {
20673 cache = isObject(config.cache) ? config.cache
20674 : isObject(defaults.cache) ? defaults.cache
20679 cachedResp = cache.get(url);
20680 if (isDefined(cachedResp)) {
20681 if (isPromiseLike(cachedResp)) {
20682 // cached request has already been sent, but there is no response yet
20683 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
20685 // serving from cache
20686 if (isArray(cachedResp)) {
20687 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
20689 resolvePromise(cachedResp, 200, {}, 'OK');
20693 // put the promise for the non-transformed response into cache as a placeholder
20694 cache.put(url, promise);
20699 // if we won't have the response in cache, set the xsrf headers and
20700 // send the request to the backend
20701 if (isUndefined(cachedResp)) {
20702 var xsrfValue = urlIsSameOrigin(config.url)
20703 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
20706 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
20709 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
20710 config.withCredentials, config.responseType,
20711 createApplyHandlers(config.eventHandlers),
20712 createApplyHandlers(config.uploadEventHandlers));
20717 function createApplyHandlers(eventHandlers) {
20718 if (eventHandlers) {
20719 var applyHandlers = {};
20720 forEach(eventHandlers, function(eventHandler, key) {
20721 applyHandlers[key] = function(event) {
20722 if (useApplyAsync) {
20723 $rootScope.$applyAsync(callEventHandler);
20724 } else if ($rootScope.$$phase) {
20725 callEventHandler();
20727 $rootScope.$apply(callEventHandler);
20730 function callEventHandler() {
20731 eventHandler(event);
20735 return applyHandlers;
20741 * Callback registered to $httpBackend():
20742 * - caches the response if desired
20743 * - resolves the raw $http promise
20746 function done(status, response, headersString, statusText) {
20748 if (isSuccess(status)) {
20749 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
20751 // remove promise from the cache
20756 function resolveHttpPromise() {
20757 resolvePromise(response, status, headersString, statusText);
20760 if (useApplyAsync) {
20761 $rootScope.$applyAsync(resolveHttpPromise);
20763 resolveHttpPromise();
20764 if (!$rootScope.$$phase) $rootScope.$apply();
20770 * Resolves the raw $http promise.
20772 function resolvePromise(response, status, headers, statusText) {
20773 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
20774 status = status >= -1 ? status : 0;
20776 (isSuccess(status) ? deferred.resolve : deferred.reject)({
20779 headers: headersGetter(headers),
20781 statusText: statusText
20785 function resolvePromiseWithResult(result) {
20786 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
20789 function removePendingReq() {
20790 var idx = $http.pendingRequests.indexOf(config);
20791 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
20796 function buildUrl(url, serializedParams) {
20797 if (serializedParams.length > 0) {
20798 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
20807 * @name $xhrFactory
20810 * Factory function used to create XMLHttpRequest objects.
20812 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
20815 * angular.module('myApp', [])
20816 * .factory('$xhrFactory', function() {
20817 * return function createXhr(method, url) {
20818 * return new window.XMLHttpRequest({mozSystem: true});
20823 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
20824 * @param {string} url URL of the request.
20826 function $xhrFactoryProvider() {
20827 this.$get = function() {
20828 return function createXhr() {
20829 return new window.XMLHttpRequest();
20836 * @name $httpBackend
20837 * @requires $window
20838 * @requires $document
20839 * @requires $xhrFactory
20842 * HTTP backend used by the {@link ng.$http service} that delegates to
20843 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
20845 * You should never need to use this service directly, instead use the higher-level abstractions:
20846 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
20848 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
20849 * $httpBackend} which can be trained with responses.
20851 function $HttpBackendProvider() {
20852 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
20853 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
20857 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
20858 // TODO(vojta): fix the signature
20859 return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
20860 $browser.$$incOutstandingRequestCount();
20861 url = url || $browser.url();
20863 if (lowercase(method) == 'jsonp') {
20864 var callbackId = '_' + (callbacks.counter++).toString(36);
20865 callbacks[callbackId] = function(data) {
20866 callbacks[callbackId].data = data;
20867 callbacks[callbackId].called = true;
20870 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
20871 callbackId, function(status, text) {
20872 completeRequest(callback, status, callbacks[callbackId].data, "", text);
20873 callbacks[callbackId] = noop;
20877 var xhr = createXhr(method, url);
20879 xhr.open(method, url, true);
20880 forEach(headers, function(value, key) {
20881 if (isDefined(value)) {
20882 xhr.setRequestHeader(key, value);
20886 xhr.onload = function requestLoaded() {
20887 var statusText = xhr.statusText || '';
20889 // responseText is the old-school way of retrieving response (supported by IE9)
20890 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
20891 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
20893 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
20894 var status = xhr.status === 1223 ? 204 : xhr.status;
20896 // fix status code when it is 0 (0 status is undocumented).
20897 // Occurs when accessing file resources or on Android 4.1 stock browser
20898 // while retrieving files from application cache.
20899 if (status === 0) {
20900 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
20903 completeRequest(callback,
20906 xhr.getAllResponseHeaders(),
20910 var requestError = function() {
20911 // The response is always empty
20912 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
20913 completeRequest(callback, -1, null, null, '');
20916 xhr.onerror = requestError;
20917 xhr.onabort = requestError;
20919 forEach(eventHandlers, function(value, key) {
20920 xhr.addEventListener(key, value);
20923 forEach(uploadEventHandlers, function(value, key) {
20924 xhr.upload.addEventListener(key, value);
20927 if (withCredentials) {
20928 xhr.withCredentials = true;
20931 if (responseType) {
20933 xhr.responseType = responseType;
20935 // WebKit added support for the json responseType value on 09/03/2013
20936 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
20937 // known to throw when setting the value "json" as the response type. Other older
20938 // browsers implementing the responseType
20940 // The json response type can be ignored if not supported, because JSON payloads are
20941 // parsed on the client-side regardless.
20942 if (responseType !== 'json') {
20948 xhr.send(isUndefined(post) ? null : post);
20952 var timeoutId = $browserDefer(timeoutRequest, timeout);
20953 } else if (isPromiseLike(timeout)) {
20954 timeout.then(timeoutRequest);
20958 function timeoutRequest() {
20959 jsonpDone && jsonpDone();
20960 xhr && xhr.abort();
20963 function completeRequest(callback, status, response, headersString, statusText) {
20964 // cancel timeout and subsequent timeout promise resolution
20965 if (isDefined(timeoutId)) {
20966 $browserDefer.cancel(timeoutId);
20968 jsonpDone = xhr = null;
20970 callback(status, response, headersString, statusText);
20971 $browser.$$completeOutstandingRequest(noop);
20975 function jsonpReq(url, callbackId, done) {
20976 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
20977 // - fetches local scripts via XHR and evals them
20978 // - adds and immediately removes script elements from the document
20979 var script = rawDocument.createElement('script'), callback = null;
20980 script.type = "text/javascript";
20982 script.async = true;
20984 callback = function(event) {
20985 removeEventListenerFn(script, "load", callback);
20986 removeEventListenerFn(script, "error", callback);
20987 rawDocument.body.removeChild(script);
20990 var text = "unknown";
20993 if (event.type === "load" && !callbacks[callbackId].called) {
20994 event = { type: "error" };
20997 status = event.type === "error" ? 404 : 200;
21001 done(status, text);
21005 addEventListenerFn(script, "load", callback);
21006 addEventListenerFn(script, "error", callback);
21007 rawDocument.body.appendChild(script);
21012 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
21013 $interpolateMinErr.throwNoconcat = function(text) {
21014 throw $interpolateMinErr('noconcat',
21015 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
21016 "interpolations that concatenate multiple expressions when a trusted value is " +
21017 "required. See http://docs.angularjs.org/api/ng.$sce", text);
21020 $interpolateMinErr.interr = function(text, err) {
21021 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
21026 * @name $interpolateProvider
21030 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
21032 * <div class="alert alert-danger">
21033 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
21034 * template within a Python Jinja template (or any other template language). Mixing templating
21035 * languages is **very dangerous**. The embedding template language will not safely escape Angular
21036 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
21041 <example name="custom-interpolation-markup" module="customInterpolationApp">
21042 <file name="index.html">
21044 var customInterpolationApp = angular.module('customInterpolationApp', []);
21046 customInterpolationApp.config(function($interpolateProvider) {
21047 $interpolateProvider.startSymbol('//');
21048 $interpolateProvider.endSymbol('//');
21052 customInterpolationApp.controller('DemoController', function() {
21053 this.label = "This binding is brought you by // interpolation symbols.";
21056 <div ng-controller="DemoController as demo">
21060 <file name="protractor.js" type="protractor">
21061 it('should interpolate binding with custom symbols', function() {
21062 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
21067 function $InterpolateProvider() {
21068 var startSymbol = '{{';
21069 var endSymbol = '}}';
21073 * @name $interpolateProvider#startSymbol
21075 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
21077 * @param {string=} value new value to set the starting symbol to.
21078 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
21080 this.startSymbol = function(value) {
21082 startSymbol = value;
21085 return startSymbol;
21091 * @name $interpolateProvider#endSymbol
21093 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
21095 * @param {string=} value new value to set the ending symbol to.
21096 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
21098 this.endSymbol = function(value) {
21108 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
21109 var startSymbolLength = startSymbol.length,
21110 endSymbolLength = endSymbol.length,
21111 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
21112 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
21114 function escape(ch) {
21115 return '\\\\\\' + ch;
21118 function unescapeText(text) {
21119 return text.replace(escapedStartRegexp, startSymbol).
21120 replace(escapedEndRegexp, endSymbol);
21123 function stringify(value) {
21124 if (value == null) { // null || undefined
21127 switch (typeof value) {
21131 value = '' + value;
21134 value = toJson(value);
21140 //TODO: this is the same as the constantWatchDelegate in parse.js
21141 function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
21143 return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
21145 return constantInterp(scope);
21146 }, listener, objectEquality);
21151 * @name $interpolate
21159 * Compiles a string with markup into an interpolation function. This service is used by the
21160 * HTML {@link ng.$compile $compile} service for data binding. See
21161 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
21162 * interpolation markup.
21166 * var $interpolate = ...; // injected
21167 * var exp = $interpolate('Hello {{name | uppercase}}!');
21168 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
21171 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
21172 * `true`, the interpolation function will return `undefined` unless all embedded expressions
21173 * evaluate to a value other than `undefined`.
21176 * var $interpolate = ...; // injected
21177 * var context = {greeting: 'Hello', name: undefined };
21179 * // default "forgiving" mode
21180 * var exp = $interpolate('{{greeting}} {{name}}!');
21181 * expect(exp(context)).toEqual('Hello !');
21183 * // "allOrNothing" mode
21184 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
21185 * expect(exp(context)).toBeUndefined();
21186 * context.name = 'Angular';
21187 * expect(exp(context)).toEqual('Hello Angular!');
21190 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
21192 * ####Escaped Interpolation
21193 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
21194 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
21195 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
21198 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
21199 * degree, while also enabling code examples to work without relying on the
21200 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
21202 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
21203 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
21204 * interpolation start/end markers with their escaped counterparts.**
21206 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
21207 * output when the $interpolate service processes the text. So, for HTML elements interpolated
21208 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
21209 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
21210 * this is typically useful only when user-data is used in rendering a template from the server, or
21211 * when otherwise untrusted data is used by a directive.
21214 * <file name="index.html">
21215 * <div ng-init="username='A user'">
21216 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
21218 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
21219 * application, but fails to accomplish their task, because the server has correctly
21220 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
21222 * <p>Instead, the result of the attempted script injection is visible, and can be removed
21223 * from the database by an administrator.</p>
21228 * @param {string} text The text with markup to interpolate.
21229 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
21230 * embedded expression in order to return an interpolation function. Strings with no
21231 * embedded expression will return null for the interpolation function.
21232 * @param {string=} trustedContext when provided, the returned function passes the interpolated
21233 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
21234 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
21235 * provides Strict Contextual Escaping for details.
21236 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
21237 * unless all embedded expressions evaluate to a value other than `undefined`.
21238 * @returns {function(context)} an interpolation function which is used to compute the
21239 * interpolated string. The function has these parameters:
21241 * - `context`: evaluation context for all expressions embedded in the interpolated text
21243 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
21244 // Provide a quick exit and simplified result function for text with no interpolation
21245 if (!text.length || text.indexOf(startSymbol) === -1) {
21246 var constantInterp;
21247 if (!mustHaveExpression) {
21248 var unescapedText = unescapeText(text);
21249 constantInterp = valueFn(unescapedText);
21250 constantInterp.exp = text;
21251 constantInterp.expressions = [];
21252 constantInterp.$$watchDelegate = constantWatchDelegate;
21254 return constantInterp;
21257 allOrNothing = !!allOrNothing;
21263 textLength = text.length,
21266 expressionPositions = [];
21268 while (index < textLength) {
21269 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
21270 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
21271 if (index !== startIndex) {
21272 concat.push(unescapeText(text.substring(index, startIndex)));
21274 exp = text.substring(startIndex + startSymbolLength, endIndex);
21275 expressions.push(exp);
21276 parseFns.push($parse(exp, parseStringifyInterceptor));
21277 index = endIndex + endSymbolLength;
21278 expressionPositions.push(concat.length);
21281 // we did not find an interpolation, so we have to add the remainder to the separators array
21282 if (index !== textLength) {
21283 concat.push(unescapeText(text.substring(index)));
21289 // Concatenating expressions makes it hard to reason about whether some combination of
21290 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
21291 // single expression be used for iframe[src], object[src], etc., we ensure that the value
21292 // that's used is assigned or constructed by some JS code somewhere that is more testable or
21293 // make it obvious that you bound the value to some user controlled value. This helps reduce
21294 // the load when auditing for XSS issues.
21295 if (trustedContext && concat.length > 1) {
21296 $interpolateMinErr.throwNoconcat(text);
21299 if (!mustHaveExpression || expressions.length) {
21300 var compute = function(values) {
21301 for (var i = 0, ii = expressions.length; i < ii; i++) {
21302 if (allOrNothing && isUndefined(values[i])) return;
21303 concat[expressionPositions[i]] = values[i];
21305 return concat.join('');
21308 var getValue = function(value) {
21309 return trustedContext ?
21310 $sce.getTrusted(trustedContext, value) :
21311 $sce.valueOf(value);
21314 return extend(function interpolationFn(context) {
21316 var ii = expressions.length;
21317 var values = new Array(ii);
21320 for (; i < ii; i++) {
21321 values[i] = parseFns[i](context);
21324 return compute(values);
21326 $exceptionHandler($interpolateMinErr.interr(text, err));
21330 // all of these properties are undocumented for now
21331 exp: text, //just for compatibility with regular watchers created via $watch
21332 expressions: expressions,
21333 $$watchDelegate: function(scope, listener) {
21335 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
21336 var currValue = compute(values);
21337 if (isFunction(listener)) {
21338 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
21340 lastValue = currValue;
21346 function parseStringifyInterceptor(value) {
21348 value = getValue(value);
21349 return allOrNothing && !isDefined(value) ? value : stringify(value);
21351 $exceptionHandler($interpolateMinErr.interr(text, err));
21359 * @name $interpolate#startSymbol
21361 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
21363 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
21366 * @returns {string} start symbol.
21368 $interpolate.startSymbol = function() {
21369 return startSymbol;
21375 * @name $interpolate#endSymbol
21377 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
21379 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
21382 * @returns {string} end symbol.
21384 $interpolate.endSymbol = function() {
21388 return $interpolate;
21392 function $IntervalProvider() {
21393 this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
21394 function($rootScope, $window, $q, $$q, $browser) {
21395 var intervals = {};
21403 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
21406 * The return value of registering an interval function is a promise. This promise will be
21407 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
21408 * run indefinitely if `count` is not defined. The value of the notification will be the
21409 * number of iterations that have run.
21410 * To cancel an interval, call `$interval.cancel(promise)`.
21412 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
21413 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
21416 * <div class="alert alert-warning">
21417 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
21418 * with them. In particular they are not automatically destroyed when a controller's scope or a
21419 * directive's element are destroyed.
21420 * You should take this into consideration and make sure to always cancel the interval at the
21421 * appropriate moment. See the example below for more details on how and when to do this.
21424 * @param {function()} fn A function that should be called repeatedly.
21425 * @param {number} delay Number of milliseconds between each function call.
21426 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
21428 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
21429 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
21430 * @param {...*=} Pass additional parameters to the executed function.
21431 * @returns {promise} A promise which will be notified on each iteration.
21434 * <example module="intervalExample">
21435 * <file name="index.html">
21437 * angular.module('intervalExample', [])
21438 * .controller('ExampleController', ['$scope', '$interval',
21439 * function($scope, $interval) {
21440 * $scope.format = 'M/d/yy h:mm:ss a';
21441 * $scope.blood_1 = 100;
21442 * $scope.blood_2 = 120;
21445 * $scope.fight = function() {
21446 * // Don't start a new fight if we are already fighting
21447 * if ( angular.isDefined(stop) ) return;
21449 * stop = $interval(function() {
21450 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
21451 * $scope.blood_1 = $scope.blood_1 - 3;
21452 * $scope.blood_2 = $scope.blood_2 - 4;
21454 * $scope.stopFight();
21459 * $scope.stopFight = function() {
21460 * if (angular.isDefined(stop)) {
21461 * $interval.cancel(stop);
21462 * stop = undefined;
21466 * $scope.resetFight = function() {
21467 * $scope.blood_1 = 100;
21468 * $scope.blood_2 = 120;
21471 * $scope.$on('$destroy', function() {
21472 * // Make sure that the interval is destroyed too
21473 * $scope.stopFight();
21476 * // Register the 'myCurrentTime' directive factory method.
21477 * // We inject $interval and dateFilter service since the factory method is DI.
21478 * .directive('myCurrentTime', ['$interval', 'dateFilter',
21479 * function($interval, dateFilter) {
21480 * // return the directive link function. (compile function not needed)
21481 * return function(scope, element, attrs) {
21482 * var format, // date format
21483 * stopTime; // so that we can cancel the time updates
21485 * // used to update the UI
21486 * function updateTime() {
21487 * element.text(dateFilter(new Date(), format));
21490 * // watch the expression, and update the UI on change.
21491 * scope.$watch(attrs.myCurrentTime, function(value) {
21496 * stopTime = $interval(updateTime, 1000);
21498 * // listen on DOM destroy (removal) event, and cancel the next UI update
21499 * // to prevent updating time after the DOM element was removed.
21500 * element.on('$destroy', function() {
21501 * $interval.cancel(stopTime);
21508 * <div ng-controller="ExampleController">
21509 * <label>Date format: <input ng-model="format"></label> <hr/>
21510 * Current time is: <span my-current-time="format"></span>
21512 * Blood 1 : <font color='red'>{{blood_1}}</font>
21513 * Blood 2 : <font color='red'>{{blood_2}}</font>
21514 * <button type="button" data-ng-click="fight()">Fight</button>
21515 * <button type="button" data-ng-click="stopFight()">StopFight</button>
21516 * <button type="button" data-ng-click="resetFight()">resetFight</button>
21523 function interval(fn, delay, count, invokeApply) {
21524 var hasParams = arguments.length > 4,
21525 args = hasParams ? sliceArgs(arguments, 4) : [],
21526 setInterval = $window.setInterval,
21527 clearInterval = $window.clearInterval,
21529 skipApply = (isDefined(invokeApply) && !invokeApply),
21530 deferred = (skipApply ? $$q : $q).defer(),
21531 promise = deferred.promise;
21533 count = isDefined(count) ? count : 0;
21535 promise.$$intervalId = setInterval(function tick() {
21537 $browser.defer(callback);
21539 $rootScope.$evalAsync(callback);
21541 deferred.notify(iteration++);
21543 if (count > 0 && iteration >= count) {
21544 deferred.resolve(iteration);
21545 clearInterval(promise.$$intervalId);
21546 delete intervals[promise.$$intervalId];
21549 if (!skipApply) $rootScope.$apply();
21553 intervals[promise.$$intervalId] = deferred;
21557 function callback() {
21561 fn.apply(null, args);
21569 * @name $interval#cancel
21572 * Cancels a task associated with the `promise`.
21574 * @param {Promise=} promise returned by the `$interval` function.
21575 * @returns {boolean} Returns `true` if the task was successfully canceled.
21577 interval.cancel = function(promise) {
21578 if (promise && promise.$$intervalId in intervals) {
21579 intervals[promise.$$intervalId].reject('canceled');
21580 $window.clearInterval(promise.$$intervalId);
21581 delete intervals[promise.$$intervalId];
21596 * $locale service provides localization rules for various Angular components. As of right now the
21597 * only public api is:
21599 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
21602 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
21603 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
21604 var $locationMinErr = minErr('$location');
21608 * Encode path using encodeUriSegment, ignoring forward slashes
21610 * @param {string} path Path to encode
21611 * @returns {string}
21613 function encodePath(path) {
21614 var segments = path.split('/'),
21615 i = segments.length;
21618 segments[i] = encodeUriSegment(segments[i]);
21621 return segments.join('/');
21624 function parseAbsoluteUrl(absoluteUrl, locationObj) {
21625 var parsedUrl = urlResolve(absoluteUrl);
21627 locationObj.$$protocol = parsedUrl.protocol;
21628 locationObj.$$host = parsedUrl.hostname;
21629 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
21633 function parseAppUrl(relativeUrl, locationObj) {
21634 var prefixed = (relativeUrl.charAt(0) !== '/');
21636 relativeUrl = '/' + relativeUrl;
21638 var match = urlResolve(relativeUrl);
21639 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
21640 match.pathname.substring(1) : match.pathname);
21641 locationObj.$$search = parseKeyValue(match.search);
21642 locationObj.$$hash = decodeURIComponent(match.hash);
21644 // make sure path starts with '/';
21645 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
21646 locationObj.$$path = '/' + locationObj.$$path;
21653 * @param {string} begin
21654 * @param {string} whole
21655 * @returns {string} returns text from whole after begin or undefined if it does not begin with
21658 function beginsWith(begin, whole) {
21659 if (whole.indexOf(begin) === 0) {
21660 return whole.substr(begin.length);
21665 function stripHash(url) {
21666 var index = url.indexOf('#');
21667 return index == -1 ? url : url.substr(0, index);
21670 function trimEmptyHash(url) {
21671 return url.replace(/(#.+)|#$/, '$1');
21675 function stripFile(url) {
21676 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
21679 /* return the server only (scheme://host:port) */
21680 function serverBase(url) {
21681 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
21686 * LocationHtml5Url represents an url
21687 * This object is exposed as $location service when HTML5 mode is enabled and supported
21690 * @param {string} appBase application base URL
21691 * @param {string} appBaseNoFile application base URL stripped of any filename
21692 * @param {string} basePrefix url path prefix
21694 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
21695 this.$$html5 = true;
21696 basePrefix = basePrefix || '';
21697 parseAbsoluteUrl(appBase, this);
21701 * Parse given html5 (regular) url string into properties
21702 * @param {string} url HTML5 url
21705 this.$$parse = function(url) {
21706 var pathUrl = beginsWith(appBaseNoFile, url);
21707 if (!isString(pathUrl)) {
21708 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
21712 parseAppUrl(pathUrl, this);
21714 if (!this.$$path) {
21722 * Compose url and update `absUrl` property
21725 this.$$compose = function() {
21726 var search = toKeyValue(this.$$search),
21727 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
21729 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
21730 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
21733 this.$$parseLinkUrl = function(url, relHref) {
21734 if (relHref && relHref[0] === '#') {
21735 // special case for links to hash fragments:
21736 // keep the old url and only replace the hash fragment
21737 this.hash(relHref.slice(1));
21740 var appUrl, prevAppUrl;
21743 if (isDefined(appUrl = beginsWith(appBase, url))) {
21744 prevAppUrl = appUrl;
21745 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
21746 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
21748 rewrittenUrl = appBase + prevAppUrl;
21750 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
21751 rewrittenUrl = appBaseNoFile + appUrl;
21752 } else if (appBaseNoFile == url + '/') {
21753 rewrittenUrl = appBaseNoFile;
21755 if (rewrittenUrl) {
21756 this.$$parse(rewrittenUrl);
21758 return !!rewrittenUrl;
21764 * LocationHashbangUrl represents url
21765 * This object is exposed as $location service when developer doesn't opt into html5 mode.
21766 * It also serves as the base class for html5 mode fallback on legacy browsers.
21769 * @param {string} appBase application base URL
21770 * @param {string} appBaseNoFile application base URL stripped of any filename
21771 * @param {string} hashPrefix hashbang prefix
21773 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
21775 parseAbsoluteUrl(appBase, this);
21779 * Parse given hashbang url into properties
21780 * @param {string} url Hashbang url
21783 this.$$parse = function(url) {
21784 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
21785 var withoutHashUrl;
21787 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
21789 // The rest of the url starts with a hash so we have
21790 // got either a hashbang path or a plain hash fragment
21791 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
21792 if (isUndefined(withoutHashUrl)) {
21793 // There was no hashbang prefix so we just have a hash fragment
21794 withoutHashUrl = withoutBaseUrl;
21798 // There was no hashbang path nor hash fragment:
21799 // If we are in HTML5 mode we use what is left as the path;
21800 // Otherwise we ignore what is left
21801 if (this.$$html5) {
21802 withoutHashUrl = withoutBaseUrl;
21804 withoutHashUrl = '';
21805 if (isUndefined(withoutBaseUrl)) {
21812 parseAppUrl(withoutHashUrl, this);
21814 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
21819 * In Windows, on an anchor node on documents loaded from
21820 * the filesystem, the browser will return a pathname
21821 * prefixed with the drive name ('/C:/path') when a
21822 * pathname without a drive is set:
21823 * * a.setAttribute('href', '/foo')
21824 * * a.pathname === '/C:/foo' //true
21826 * Inside of Angular, we're always using pathnames that
21827 * do not include drive names for routing.
21829 function removeWindowsDriveName(path, url, base) {
21831 Matches paths for file protocol on windows,
21832 such as /C:/foo/bar, and captures only /foo/bar.
21834 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
21836 var firstPathSegmentMatch;
21838 //Get the relative path from the input URL.
21839 if (url.indexOf(base) === 0) {
21840 url = url.replace(base, '');
21843 // The input URL intentionally contains a first path segment that ends with a colon.
21844 if (windowsFilePathExp.exec(url)) {
21848 firstPathSegmentMatch = windowsFilePathExp.exec(path);
21849 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
21854 * Compose hashbang url and update `absUrl` property
21857 this.$$compose = function() {
21858 var search = toKeyValue(this.$$search),
21859 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
21861 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
21862 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
21865 this.$$parseLinkUrl = function(url, relHref) {
21866 if (stripHash(appBase) == stripHash(url)) {
21876 * LocationHashbangUrl represents url
21877 * This object is exposed as $location service when html5 history api is enabled but the browser
21878 * does not support it.
21881 * @param {string} appBase application base URL
21882 * @param {string} appBaseNoFile application base URL stripped of any filename
21883 * @param {string} hashPrefix hashbang prefix
21885 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
21886 this.$$html5 = true;
21887 LocationHashbangUrl.apply(this, arguments);
21889 this.$$parseLinkUrl = function(url, relHref) {
21890 if (relHref && relHref[0] === '#') {
21891 // special case for links to hash fragments:
21892 // keep the old url and only replace the hash fragment
21893 this.hash(relHref.slice(1));
21900 if (appBase == stripHash(url)) {
21901 rewrittenUrl = url;
21902 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
21903 rewrittenUrl = appBase + hashPrefix + appUrl;
21904 } else if (appBaseNoFile === url + '/') {
21905 rewrittenUrl = appBaseNoFile;
21907 if (rewrittenUrl) {
21908 this.$$parse(rewrittenUrl);
21910 return !!rewrittenUrl;
21913 this.$$compose = function() {
21914 var search = toKeyValue(this.$$search),
21915 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
21917 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
21918 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
21919 this.$$absUrl = appBase + hashPrefix + this.$$url;
21925 var locationPrototype = {
21928 * Are we in html5 mode?
21934 * Has any change been replacing?
21941 * @name $location#absUrl
21944 * This method is getter only.
21946 * Return full url representation with all segments encoded according to rules specified in
21947 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
21951 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21952 * var absUrl = $location.absUrl();
21953 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
21956 * @return {string} full url
21958 absUrl: locationGetter('$$absUrl'),
21962 * @name $location#url
21965 * This method is getter / setter.
21967 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
21969 * Change path, search and hash, when called with parameter and return `$location`.
21973 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21974 * var url = $location.url();
21975 * // => "/some/path?foo=bar&baz=xoxo"
21978 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
21979 * @return {string} url
21981 url: function(url) {
21982 if (isUndefined(url)) {
21986 var match = PATH_MATCH.exec(url);
21987 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
21988 if (match[2] || match[1] || url === '') this.search(match[3] || '');
21989 this.hash(match[5] || '');
21996 * @name $location#protocol
21999 * This method is getter only.
22001 * Return protocol of current url.
22005 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
22006 * var protocol = $location.protocol();
22010 * @return {string} protocol of current url
22012 protocol: locationGetter('$$protocol'),
22016 * @name $location#host
22019 * This method is getter only.
22021 * Return host of current url.
22023 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
22027 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
22028 * var host = $location.host();
22029 * // => "example.com"
22031 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
22032 * host = $location.host();
22033 * // => "example.com"
22034 * host = location.host;
22035 * // => "example.com:8080"
22038 * @return {string} host of current url.
22040 host: locationGetter('$$host'),
22044 * @name $location#port
22047 * This method is getter only.
22049 * Return port of current url.
22053 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
22054 * var port = $location.port();
22058 * @return {Number} port
22060 port: locationGetter('$$port'),
22064 * @name $location#path
22067 * This method is getter / setter.
22069 * Return path of current url when called without any parameter.
22071 * Change path when called with parameter and return `$location`.
22073 * Note: Path should always begin with forward slash (/), this method will add the forward slash
22074 * if it is missing.
22078 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
22079 * var path = $location.path();
22080 * // => "/some/path"
22083 * @param {(string|number)=} path New path
22084 * @return {string} path
22086 path: locationGetterSetter('$$path', function(path) {
22087 path = path !== null ? path.toString() : '';
22088 return path.charAt(0) == '/' ? path : '/' + path;
22093 * @name $location#search
22096 * This method is getter / setter.
22098 * Return search part (as object) of current url when called without any parameter.
22100 * Change search part when called with parameter and return `$location`.
22104 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
22105 * var searchObject = $location.search();
22106 * // => {foo: 'bar', baz: 'xoxo'}
22108 * // set foo to 'yipee'
22109 * $location.search('foo', 'yipee');
22110 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
22113 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
22116 * When called with a single argument the method acts as a setter, setting the `search` component
22117 * of `$location` to the specified value.
22119 * If the argument is a hash object containing an array of values, these values will be encoded
22120 * as duplicate search parameters in the url.
22122 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
22123 * will override only a single search property.
22125 * If `paramValue` is an array, it will override the property of the `search` component of
22126 * `$location` specified via the first argument.
22128 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
22130 * If `paramValue` is `true`, the property specified via the first argument will be added with no
22131 * value nor trailing equal sign.
22133 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
22134 * one or more arguments returns `$location` object itself.
22136 search: function(search, paramValue) {
22137 switch (arguments.length) {
22139 return this.$$search;
22141 if (isString(search) || isNumber(search)) {
22142 search = search.toString();
22143 this.$$search = parseKeyValue(search);
22144 } else if (isObject(search)) {
22145 search = copy(search, {});
22146 // remove object undefined or null properties
22147 forEach(search, function(value, key) {
22148 if (value == null) delete search[key];
22151 this.$$search = search;
22153 throw $locationMinErr('isrcharg',
22154 'The first argument of the `$location#search()` call must be a string or an object.');
22158 if (isUndefined(paramValue) || paramValue === null) {
22159 delete this.$$search[search];
22161 this.$$search[search] = paramValue;
22171 * @name $location#hash
22174 * This method is getter / setter.
22176 * Returns the hash fragment when called without any parameters.
22178 * Changes the hash fragment when called with a parameter and returns `$location`.
22182 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
22183 * var hash = $location.hash();
22184 * // => "hashValue"
22187 * @param {(string|number)=} hash New hash fragment
22188 * @return {string} hash
22190 hash: locationGetterSetter('$$hash', function(hash) {
22191 return hash !== null ? hash.toString() : '';
22196 * @name $location#replace
22199 * If called, all changes to $location during the current `$digest` will replace the current history
22200 * record, instead of adding a new one.
22202 replace: function() {
22203 this.$$replace = true;
22208 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
22209 Location.prototype = Object.create(locationPrototype);
22213 * @name $location#state
22216 * This method is getter / setter.
22218 * Return the history state object when called without any parameter.
22220 * Change the history state object when called with one parameter and return `$location`.
22221 * The state object is later passed to `pushState` or `replaceState`.
22223 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
22224 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
22225 * older browsers (like IE9 or Android < 4.0), don't use this method.
22227 * @param {object=} state State object for pushState or replaceState
22228 * @return {object} state
22230 Location.prototype.state = function(state) {
22231 if (!arguments.length) {
22232 return this.$$state;
22235 if (Location !== LocationHtml5Url || !this.$$html5) {
22236 throw $locationMinErr('nostate', 'History API state support is available only ' +
22237 'in HTML5 mode and only in browsers supporting HTML5 History API');
22239 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
22240 // but we're changing the $$state reference to $browser.state() during the $digest
22241 // so the modification window is narrow.
22242 this.$$state = isUndefined(state) ? null : state;
22249 function locationGetter(property) {
22250 return function() {
22251 return this[property];
22256 function locationGetterSetter(property, preprocess) {
22257 return function(value) {
22258 if (isUndefined(value)) {
22259 return this[property];
22262 this[property] = preprocess(value);
22274 * @requires $rootElement
22277 * The $location service parses the URL in the browser address bar (based on the
22278 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
22279 * available to your application. Changes to the URL in the address bar are reflected into
22280 * $location service and changes to $location are reflected into the browser address bar.
22282 * **The $location service:**
22284 * - Exposes the current URL in the browser address bar, so you can
22285 * - Watch and observe the URL.
22286 * - Change the URL.
22287 * - Synchronizes the URL with the browser when the user
22288 * - Changes the address bar.
22289 * - Clicks the back or forward button (or clicks a History link).
22290 * - Clicks on a link.
22291 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
22293 * For more information see {@link guide/$location Developer Guide: Using $location}
22298 * @name $locationProvider
22300 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
22302 function $LocationProvider() {
22303 var hashPrefix = '',
22312 * @name $locationProvider#hashPrefix
22314 * @param {string=} prefix Prefix for hash part (containing path and search)
22315 * @returns {*} current value if used as getter or itself (chaining) if used as setter
22317 this.hashPrefix = function(prefix) {
22318 if (isDefined(prefix)) {
22319 hashPrefix = prefix;
22328 * @name $locationProvider#html5Mode
22330 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
22331 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
22333 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
22334 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
22335 * support `pushState`.
22336 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
22337 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
22338 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
22339 * See the {@link guide/$location $location guide for more information}
22340 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
22341 * enables/disables url rewriting for relative links.
22343 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
22345 this.html5Mode = function(mode) {
22346 if (isBoolean(mode)) {
22347 html5Mode.enabled = mode;
22349 } else if (isObject(mode)) {
22351 if (isBoolean(mode.enabled)) {
22352 html5Mode.enabled = mode.enabled;
22355 if (isBoolean(mode.requireBase)) {
22356 html5Mode.requireBase = mode.requireBase;
22359 if (isBoolean(mode.rewriteLinks)) {
22360 html5Mode.rewriteLinks = mode.rewriteLinks;
22371 * @name $location#$locationChangeStart
22372 * @eventType broadcast on root scope
22374 * Broadcasted before a URL will change.
22376 * This change can be prevented by calling
22377 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
22378 * details about event object. Upon successful change
22379 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
22381 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
22382 * the browser supports the HTML5 History API.
22384 * @param {Object} angularEvent Synthetic event object.
22385 * @param {string} newUrl New URL
22386 * @param {string=} oldUrl URL that was before it was changed.
22387 * @param {string=} newState New history state object
22388 * @param {string=} oldState History state object that was before it was changed.
22393 * @name $location#$locationChangeSuccess
22394 * @eventType broadcast on root scope
22396 * Broadcasted after a URL was changed.
22398 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
22399 * the browser supports the HTML5 History API.
22401 * @param {Object} angularEvent Synthetic event object.
22402 * @param {string} newUrl New URL
22403 * @param {string=} oldUrl URL that was before it was changed.
22404 * @param {string=} newState New history state object
22405 * @param {string=} oldState History state object that was before it was changed.
22408 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
22409 function($rootScope, $browser, $sniffer, $rootElement, $window) {
22412 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
22413 initialUrl = $browser.url(),
22416 if (html5Mode.enabled) {
22417 if (!baseHref && html5Mode.requireBase) {
22418 throw $locationMinErr('nobase',
22419 "$location in HTML5 mode requires a <base> tag to be present!");
22421 appBase = serverBase(initialUrl) + (baseHref || '/');
22422 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
22424 appBase = stripHash(initialUrl);
22425 LocationMode = LocationHashbangUrl;
22427 var appBaseNoFile = stripFile(appBase);
22429 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
22430 $location.$$parseLinkUrl(initialUrl, initialUrl);
22432 $location.$$state = $browser.state();
22434 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
22436 function setBrowserUrlWithFallback(url, replace, state) {
22437 var oldUrl = $location.url();
22438 var oldState = $location.$$state;
22440 $browser.url(url, replace, state);
22442 // Make sure $location.state() returns referentially identical (not just deeply equal)
22443 // state object; this makes possible quick checking if the state changed in the digest
22444 // loop. Checking deep equality would be too expensive.
22445 $location.$$state = $browser.state();
22447 // Restore old values if pushState fails
22448 $location.url(oldUrl);
22449 $location.$$state = oldState;
22455 $rootElement.on('click', function(event) {
22456 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
22457 // currently we open nice url link and redirect then
22459 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
22461 var elm = jqLite(event.target);
22463 // traverse the DOM up to find first A tag
22464 while (nodeName_(elm[0]) !== 'a') {
22465 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
22466 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
22469 var absHref = elm.prop('href');
22470 // get the actual href attribute - see
22471 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
22472 var relHref = elm.attr('href') || elm.attr('xlink:href');
22474 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
22475 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
22477 absHref = urlResolve(absHref.animVal).href;
22480 // Ignore when url is started with javascript: or mailto:
22481 if (IGNORE_URI_REGEXP.test(absHref)) return;
22483 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
22484 if ($location.$$parseLinkUrl(absHref, relHref)) {
22485 // We do a preventDefault for all urls that are part of the angular application,
22486 // in html5mode and also without, so that we are able to abort navigation without
22487 // getting double entries in the location history.
22488 event.preventDefault();
22489 // update location manually
22490 if ($location.absUrl() != $browser.url()) {
22491 $rootScope.$apply();
22492 // hack to work around FF6 bug 684208 when scenario runner clicks on links
22493 $window.angular['ff-684208-preventDefault'] = true;
22500 // rewrite hashbang url <> html5 url
22501 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
22502 $browser.url($location.absUrl(), true);
22505 var initializing = true;
22507 // update $location when $browser url changes
22508 $browser.onUrlChange(function(newUrl, newState) {
22510 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
22511 // If we are navigating outside of the app then force a reload
22512 $window.location.href = newUrl;
22516 $rootScope.$evalAsync(function() {
22517 var oldUrl = $location.absUrl();
22518 var oldState = $location.$$state;
22519 var defaultPrevented;
22520 newUrl = trimEmptyHash(newUrl);
22521 $location.$$parse(newUrl);
22522 $location.$$state = newState;
22524 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
22525 newState, oldState).defaultPrevented;
22527 // if the location was changed by a `$locationChangeStart` handler then stop
22528 // processing this location change
22529 if ($location.absUrl() !== newUrl) return;
22531 if (defaultPrevented) {
22532 $location.$$parse(oldUrl);
22533 $location.$$state = oldState;
22534 setBrowserUrlWithFallback(oldUrl, false, oldState);
22536 initializing = false;
22537 afterLocationChange(oldUrl, oldState);
22540 if (!$rootScope.$$phase) $rootScope.$digest();
22544 $rootScope.$watch(function $locationWatch() {
22545 var oldUrl = trimEmptyHash($browser.url());
22546 var newUrl = trimEmptyHash($location.absUrl());
22547 var oldState = $browser.state();
22548 var currentReplace = $location.$$replace;
22549 var urlOrStateChanged = oldUrl !== newUrl ||
22550 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
22552 if (initializing || urlOrStateChanged) {
22553 initializing = false;
22555 $rootScope.$evalAsync(function() {
22556 var newUrl = $location.absUrl();
22557 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
22558 $location.$$state, oldState).defaultPrevented;
22560 // if the location was changed by a `$locationChangeStart` handler then stop
22561 // processing this location change
22562 if ($location.absUrl() !== newUrl) return;
22564 if (defaultPrevented) {
22565 $location.$$parse(oldUrl);
22566 $location.$$state = oldState;
22568 if (urlOrStateChanged) {
22569 setBrowserUrlWithFallback(newUrl, currentReplace,
22570 oldState === $location.$$state ? null : $location.$$state);
22572 afterLocationChange(oldUrl, oldState);
22577 $location.$$replace = false;
22579 // we don't need to return anything because $evalAsync will make the digest loop dirty when
22580 // there is a change
22585 function afterLocationChange(oldUrl, oldState) {
22586 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
22587 $location.$$state, oldState);
22595 * @requires $window
22598 * Simple service for logging. Default implementation safely writes the message
22599 * into the browser's console (if present).
22601 * The main purpose of this service is to simplify debugging and troubleshooting.
22603 * The default is to log `debug` messages. You can use
22604 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
22607 <example module="logExample">
22608 <file name="script.js">
22609 angular.module('logExample', [])
22610 .controller('LogController', ['$scope', '$log', function($scope, $log) {
22611 $scope.$log = $log;
22612 $scope.message = 'Hello World!';
22615 <file name="index.html">
22616 <div ng-controller="LogController">
22617 <p>Reload this page with open console, enter text and hit the log button...</p>
22619 <input type="text" ng-model="message" /></label>
22620 <button ng-click="$log.log(message)">log</button>
22621 <button ng-click="$log.warn(message)">warn</button>
22622 <button ng-click="$log.info(message)">info</button>
22623 <button ng-click="$log.error(message)">error</button>
22624 <button ng-click="$log.debug(message)">debug</button>
22632 * @name $logProvider
22634 * Use the `$logProvider` to configure how the application logs messages
22636 function $LogProvider() {
22642 * @name $logProvider#debugEnabled
22644 * @param {boolean=} flag enable or disable debug level messages
22645 * @returns {*} current value if used as getter or itself (chaining) if used as setter
22647 this.debugEnabled = function(flag) {
22648 if (isDefined(flag)) {
22656 this.$get = ['$window', function($window) {
22663 * Write a log message
22665 log: consoleLog('log'),
22672 * Write an information message
22674 info: consoleLog('info'),
22681 * Write a warning message
22683 warn: consoleLog('warn'),
22690 * Write an error message
22692 error: consoleLog('error'),
22699 * Write a debug message
22701 debug: (function() {
22702 var fn = consoleLog('debug');
22704 return function() {
22706 fn.apply(self, arguments);
22712 function formatError(arg) {
22713 if (arg instanceof Error) {
22715 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
22716 ? 'Error: ' + arg.message + '\n' + arg.stack
22718 } else if (arg.sourceURL) {
22719 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
22725 function consoleLog(type) {
22726 var console = $window.console || {},
22727 logFn = console[type] || console.log || noop,
22730 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
22731 // The reason behind this is that console.log has type "object" in IE8...
22733 hasApply = !!logFn.apply;
22737 return function() {
22739 forEach(arguments, function(arg) {
22740 args.push(formatError(arg));
22742 return logFn.apply(console, args);
22746 // we are IE which either doesn't have window.console => this is noop and we do nothing,
22747 // or we are IE where console.log doesn't have apply so we log at least first 2 args
22748 return function(arg1, arg2) {
22749 logFn(arg1, arg2 == null ? '' : arg2);
22755 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22756 * Any commits to this file should be reviewed with security in mind. *
22757 * Changes to this file can potentially create security vulnerabilities. *
22758 * An approval from 2 Core members with history of modifying *
22759 * this file is required. *
22761 * Does the change somehow allow for arbitrary javascript to be executed? *
22762 * Or allows for someone to change the prototype of built-in objects? *
22763 * Or gives undesired access to variables likes document or window? *
22764 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22766 var $parseMinErr = minErr('$parse');
22768 // Sandboxing Angular Expressions
22769 // ------------------------------
22770 // Angular expressions are generally considered safe because these expressions only have direct
22771 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
22772 // obtaining a reference to native JS functions such as the Function constructor.
22774 // As an example, consider the following Angular expression:
22776 // {}.toString.constructor('alert("evil JS code")')
22778 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
22779 // against the expression language, but not to prevent exploits that were enabled by exposing
22780 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
22781 // practice and therefore we are not even trying to protect against interaction with an object
22782 // explicitly exposed in this way.
22784 // In general, it is not possible to access a Window object from an angular expression unless a
22785 // window or some DOM object that has a reference to window is published onto a Scope.
22786 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
22789 // See https://docs.angularjs.org/guide/security
22792 function ensureSafeMemberName(name, fullExpression) {
22793 if (name === "__defineGetter__" || name === "__defineSetter__"
22794 || name === "__lookupGetter__" || name === "__lookupSetter__"
22795 || name === "__proto__") {
22796 throw $parseMinErr('isecfld',
22797 'Attempting to access a disallowed field in Angular expressions! '
22798 + 'Expression: {0}', fullExpression);
22803 function getStringValue(name) {
22804 // Property names must be strings. This means that non-string objects cannot be used
22805 // as keys in an object. Any non-string object, including a number, is typecasted
22806 // into a string via the toString method.
22807 // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
22809 // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
22810 // to a string. It's not always possible. If `name` is an object and its `toString` method is
22811 // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
22813 // TypeError: Cannot convert object to primitive value
22815 // For performance reasons, we don't catch this error here and allow it to propagate up the call
22816 // stack. Note that you'll get the same error in JavaScript if you try to access a property using
22817 // such a 'broken' object as a key.
22821 function ensureSafeObject(obj, fullExpression) {
22822 // nifty check if obj is Function that is fast and works across iframes and other contexts
22824 if (obj.constructor === obj) {
22825 throw $parseMinErr('isecfn',
22826 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
22828 } else if (// isWindow(obj)
22829 obj.window === obj) {
22830 throw $parseMinErr('isecwindow',
22831 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
22833 } else if (// isElement(obj)
22834 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
22835 throw $parseMinErr('isecdom',
22836 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
22838 } else if (// block Object so that we can't get hold of dangerous Object.* methods
22840 throw $parseMinErr('isecobj',
22841 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
22848 var CALL = Function.prototype.call;
22849 var APPLY = Function.prototype.apply;
22850 var BIND = Function.prototype.bind;
22852 function ensureSafeFunction(obj, fullExpression) {
22854 if (obj.constructor === obj) {
22855 throw $parseMinErr('isecfn',
22856 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
22858 } else if (obj === CALL || obj === APPLY || obj === BIND) {
22859 throw $parseMinErr('isecff',
22860 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
22866 function ensureSafeAssignContext(obj, fullExpression) {
22868 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
22869 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
22870 throw $parseMinErr('isecaf',
22871 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
22876 var OPERATORS = createMap();
22877 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
22878 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
22881 /////////////////////////////////////////
22887 var Lexer = function(options) {
22888 this.options = options;
22891 Lexer.prototype = {
22892 constructor: Lexer,
22894 lex: function(text) {
22899 while (this.index < this.text.length) {
22900 var ch = this.text.charAt(this.index);
22901 if (ch === '"' || ch === "'") {
22902 this.readString(ch);
22903 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
22905 } else if (this.isIdentifierStart(this.peekMultichar())) {
22907 } else if (this.is(ch, '(){}[].,;:?')) {
22908 this.tokens.push({index: this.index, text: ch});
22910 } else if (this.isWhitespace(ch)) {
22913 var ch2 = ch + this.peek();
22914 var ch3 = ch2 + this.peek(2);
22915 var op1 = OPERATORS[ch];
22916 var op2 = OPERATORS[ch2];
22917 var op3 = OPERATORS[ch3];
22918 if (op1 || op2 || op3) {
22919 var token = op3 ? ch3 : (op2 ? ch2 : ch);
22920 this.tokens.push({index: this.index, text: token, operator: true});
22921 this.index += token.length;
22923 this.throwError('Unexpected next character ', this.index, this.index + 1);
22927 return this.tokens;
22930 is: function(ch, chars) {
22931 return chars.indexOf(ch) !== -1;
22934 peek: function(i) {
22936 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
22939 isNumber: function(ch) {
22940 return ('0' <= ch && ch <= '9') && typeof ch === "string";
22943 isWhitespace: function(ch) {
22944 // IE treats non-breaking space as \u00A0
22945 return (ch === ' ' || ch === '\r' || ch === '\t' ||
22946 ch === '\n' || ch === '\v' || ch === '\u00A0');
22949 isIdentifierStart: function(ch) {
22950 return this.options.isIdentifierStart ?
22951 this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
22952 this.isValidIdentifierStart(ch);
22955 isValidIdentifierStart: function(ch) {
22956 return ('a' <= ch && ch <= 'z' ||
22957 'A' <= ch && ch <= 'Z' ||
22958 '_' === ch || ch === '$');
22961 isIdentifierContinue: function(ch) {
22962 return this.options.isIdentifierContinue ?
22963 this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
22964 this.isValidIdentifierContinue(ch);
22967 isValidIdentifierContinue: function(ch, cp) {
22968 return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
22971 codePointAt: function(ch) {
22972 if (ch.length === 1) return ch.charCodeAt(0);
22973 /*jshint bitwise: false*/
22974 return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
22975 /*jshint bitwise: true*/
22978 peekMultichar: function() {
22979 var ch = this.text.charAt(this.index);
22980 var peek = this.peek();
22984 var cp1 = ch.charCodeAt(0);
22985 var cp2 = peek.charCodeAt(0);
22986 if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
22992 isExpOperator: function(ch) {
22993 return (ch === '-' || ch === '+' || this.isNumber(ch));
22996 throwError: function(error, start, end) {
22997 end = end || this.index;
22998 var colStr = (isDefined(start)
22999 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
23001 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
23002 error, colStr, this.text);
23005 readNumber: function() {
23007 var start = this.index;
23008 while (this.index < this.text.length) {
23009 var ch = lowercase(this.text.charAt(this.index));
23010 if (ch == '.' || this.isNumber(ch)) {
23013 var peekCh = this.peek();
23014 if (ch == 'e' && this.isExpOperator(peekCh)) {
23016 } else if (this.isExpOperator(ch) &&
23017 peekCh && this.isNumber(peekCh) &&
23018 number.charAt(number.length - 1) == 'e') {
23020 } else if (this.isExpOperator(ch) &&
23021 (!peekCh || !this.isNumber(peekCh)) &&
23022 number.charAt(number.length - 1) == 'e') {
23023 this.throwError('Invalid exponent');
23034 value: Number(number)
23038 readIdent: function() {
23039 var start = this.index;
23040 this.index += this.peekMultichar().length;
23041 while (this.index < this.text.length) {
23042 var ch = this.peekMultichar();
23043 if (!this.isIdentifierContinue(ch)) {
23046 this.index += ch.length;
23050 text: this.text.slice(start, this.index),
23055 readString: function(quote) {
23056 var start = this.index;
23059 var rawString = quote;
23060 var escape = false;
23061 while (this.index < this.text.length) {
23062 var ch = this.text.charAt(this.index);
23066 var hex = this.text.substring(this.index + 1, this.index + 5);
23067 if (!hex.match(/[\da-f]{4}/i)) {
23068 this.throwError('Invalid unicode escape [\\u' + hex + ']');
23071 string += String.fromCharCode(parseInt(hex, 16));
23073 var rep = ESCAPE[ch];
23074 string = string + (rep || ch);
23077 } else if (ch === '\\') {
23079 } else if (ch === quote) {
23093 this.throwError('Unterminated quote', start);
23097 var AST = function(lexer, options) {
23098 this.lexer = lexer;
23099 this.options = options;
23102 AST.Program = 'Program';
23103 AST.ExpressionStatement = 'ExpressionStatement';
23104 AST.AssignmentExpression = 'AssignmentExpression';
23105 AST.ConditionalExpression = 'ConditionalExpression';
23106 AST.LogicalExpression = 'LogicalExpression';
23107 AST.BinaryExpression = 'BinaryExpression';
23108 AST.UnaryExpression = 'UnaryExpression';
23109 AST.CallExpression = 'CallExpression';
23110 AST.MemberExpression = 'MemberExpression';
23111 AST.Identifier = 'Identifier';
23112 AST.Literal = 'Literal';
23113 AST.ArrayExpression = 'ArrayExpression';
23114 AST.Property = 'Property';
23115 AST.ObjectExpression = 'ObjectExpression';
23116 AST.ThisExpression = 'ThisExpression';
23117 AST.LocalsExpression = 'LocalsExpression';
23119 // Internal use only
23120 AST.NGValueParameter = 'NGValueParameter';
23123 ast: function(text) {
23125 this.tokens = this.lexer.lex(text);
23127 var value = this.program();
23129 if (this.tokens.length !== 0) {
23130 this.throwError('is an unexpected token', this.tokens[0]);
23136 program: function() {
23139 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
23140 body.push(this.expressionStatement());
23141 if (!this.expect(';')) {
23142 return { type: AST.Program, body: body};
23147 expressionStatement: function() {
23148 return { type: AST.ExpressionStatement, expression: this.filterChain() };
23151 filterChain: function() {
23152 var left = this.expression();
23154 while ((token = this.expect('|'))) {
23155 left = this.filter(left);
23160 expression: function() {
23161 return this.assignment();
23164 assignment: function() {
23165 var result = this.ternary();
23166 if (this.expect('=')) {
23167 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
23172 ternary: function() {
23173 var test = this.logicalOR();
23176 if (this.expect('?')) {
23177 alternate = this.expression();
23178 if (this.consume(':')) {
23179 consequent = this.expression();
23180 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
23186 logicalOR: function() {
23187 var left = this.logicalAND();
23188 while (this.expect('||')) {
23189 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
23194 logicalAND: function() {
23195 var left = this.equality();
23196 while (this.expect('&&')) {
23197 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
23202 equality: function() {
23203 var left = this.relational();
23205 while ((token = this.expect('==','!=','===','!=='))) {
23206 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
23211 relational: function() {
23212 var left = this.additive();
23214 while ((token = this.expect('<', '>', '<=', '>='))) {
23215 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
23220 additive: function() {
23221 var left = this.multiplicative();
23223 while ((token = this.expect('+','-'))) {
23224 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
23229 multiplicative: function() {
23230 var left = this.unary();
23232 while ((token = this.expect('*','/','%'))) {
23233 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
23238 unary: function() {
23240 if ((token = this.expect('+', '-', '!'))) {
23241 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
23243 return this.primary();
23247 primary: function() {
23249 if (this.expect('(')) {
23250 primary = this.filterChain();
23252 } else if (this.expect('[')) {
23253 primary = this.arrayDeclaration();
23254 } else if (this.expect('{')) {
23255 primary = this.object();
23256 } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
23257 primary = copy(this.selfReferential[this.consume().text]);
23258 } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
23259 primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
23260 } else if (this.peek().identifier) {
23261 primary = this.identifier();
23262 } else if (this.peek().constant) {
23263 primary = this.constant();
23265 this.throwError('not a primary expression', this.peek());
23269 while ((next = this.expect('(', '[', '.'))) {
23270 if (next.text === '(') {
23271 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
23273 } else if (next.text === '[') {
23274 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
23276 } else if (next.text === '.') {
23277 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
23279 this.throwError('IMPOSSIBLE');
23285 filter: function(baseExpression) {
23286 var args = [baseExpression];
23287 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
23289 while (this.expect(':')) {
23290 args.push(this.expression());
23296 parseArguments: function() {
23298 if (this.peekToken().text !== ')') {
23300 args.push(this.expression());
23301 } while (this.expect(','));
23306 identifier: function() {
23307 var token = this.consume();
23308 if (!token.identifier) {
23309 this.throwError('is not a valid identifier', token);
23311 return { type: AST.Identifier, name: token.text };
23314 constant: function() {
23315 // TODO check that it is a constant
23316 return { type: AST.Literal, value: this.consume().value };
23319 arrayDeclaration: function() {
23321 if (this.peekToken().text !== ']') {
23323 if (this.peek(']')) {
23324 // Support trailing commas per ES5.1.
23327 elements.push(this.expression());
23328 } while (this.expect(','));
23332 return { type: AST.ArrayExpression, elements: elements };
23335 object: function() {
23336 var properties = [], property;
23337 if (this.peekToken().text !== '}') {
23339 if (this.peek('}')) {
23340 // Support trailing commas per ES5.1.
23343 property = {type: AST.Property, kind: 'init'};
23344 if (this.peek().constant) {
23345 property.key = this.constant();
23346 } else if (this.peek().identifier) {
23347 property.key = this.identifier();
23349 this.throwError("invalid key", this.peek());
23352 property.value = this.expression();
23353 properties.push(property);
23354 } while (this.expect(','));
23358 return {type: AST.ObjectExpression, properties: properties };
23361 throwError: function(msg, token) {
23362 throw $parseMinErr('syntax',
23363 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
23364 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
23367 consume: function(e1) {
23368 if (this.tokens.length === 0) {
23369 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
23372 var token = this.expect(e1);
23374 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
23379 peekToken: function() {
23380 if (this.tokens.length === 0) {
23381 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
23383 return this.tokens[0];
23386 peek: function(e1, e2, e3, e4) {
23387 return this.peekAhead(0, e1, e2, e3, e4);
23390 peekAhead: function(i, e1, e2, e3, e4) {
23391 if (this.tokens.length > i) {
23392 var token = this.tokens[i];
23393 var t = token.text;
23394 if (t === e1 || t === e2 || t === e3 || t === e4 ||
23395 (!e1 && !e2 && !e3 && !e4)) {
23402 expect: function(e1, e2, e3, e4) {
23403 var token = this.peek(e1, e2, e3, e4);
23405 this.tokens.shift();
23412 'this': {type: AST.ThisExpression },
23413 '$locals': {type: AST.LocalsExpression }
23417 function ifDefined(v, d) {
23418 return typeof v !== 'undefined' ? v : d;
23421 function plusFn(l, r) {
23422 if (typeof l === 'undefined') return r;
23423 if (typeof r === 'undefined') return l;
23427 function isStateless($filter, filterName) {
23428 var fn = $filter(filterName);
23429 return !fn.$stateful;
23432 function findConstantAndWatchExpressions(ast, $filter) {
23435 switch (ast.type) {
23437 allConstants = true;
23438 forEach(ast.body, function(expr) {
23439 findConstantAndWatchExpressions(expr.expression, $filter);
23440 allConstants = allConstants && expr.expression.constant;
23442 ast.constant = allConstants;
23445 ast.constant = true;
23448 case AST.UnaryExpression:
23449 findConstantAndWatchExpressions(ast.argument, $filter);
23450 ast.constant = ast.argument.constant;
23451 ast.toWatch = ast.argument.toWatch;
23453 case AST.BinaryExpression:
23454 findConstantAndWatchExpressions(ast.left, $filter);
23455 findConstantAndWatchExpressions(ast.right, $filter);
23456 ast.constant = ast.left.constant && ast.right.constant;
23457 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
23459 case AST.LogicalExpression:
23460 findConstantAndWatchExpressions(ast.left, $filter);
23461 findConstantAndWatchExpressions(ast.right, $filter);
23462 ast.constant = ast.left.constant && ast.right.constant;
23463 ast.toWatch = ast.constant ? [] : [ast];
23465 case AST.ConditionalExpression:
23466 findConstantAndWatchExpressions(ast.test, $filter);
23467 findConstantAndWatchExpressions(ast.alternate, $filter);
23468 findConstantAndWatchExpressions(ast.consequent, $filter);
23469 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
23470 ast.toWatch = ast.constant ? [] : [ast];
23472 case AST.Identifier:
23473 ast.constant = false;
23474 ast.toWatch = [ast];
23476 case AST.MemberExpression:
23477 findConstantAndWatchExpressions(ast.object, $filter);
23478 if (ast.computed) {
23479 findConstantAndWatchExpressions(ast.property, $filter);
23481 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
23482 ast.toWatch = [ast];
23484 case AST.CallExpression:
23485 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
23487 forEach(ast.arguments, function(expr) {
23488 findConstantAndWatchExpressions(expr, $filter);
23489 allConstants = allConstants && expr.constant;
23490 if (!expr.constant) {
23491 argsToWatch.push.apply(argsToWatch, expr.toWatch);
23494 ast.constant = allConstants;
23495 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
23497 case AST.AssignmentExpression:
23498 findConstantAndWatchExpressions(ast.left, $filter);
23499 findConstantAndWatchExpressions(ast.right, $filter);
23500 ast.constant = ast.left.constant && ast.right.constant;
23501 ast.toWatch = [ast];
23503 case AST.ArrayExpression:
23504 allConstants = true;
23506 forEach(ast.elements, function(expr) {
23507 findConstantAndWatchExpressions(expr, $filter);
23508 allConstants = allConstants && expr.constant;
23509 if (!expr.constant) {
23510 argsToWatch.push.apply(argsToWatch, expr.toWatch);
23513 ast.constant = allConstants;
23514 ast.toWatch = argsToWatch;
23516 case AST.ObjectExpression:
23517 allConstants = true;
23519 forEach(ast.properties, function(property) {
23520 findConstantAndWatchExpressions(property.value, $filter);
23521 allConstants = allConstants && property.value.constant;
23522 if (!property.value.constant) {
23523 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
23526 ast.constant = allConstants;
23527 ast.toWatch = argsToWatch;
23529 case AST.ThisExpression:
23530 ast.constant = false;
23533 case AST.LocalsExpression:
23534 ast.constant = false;
23540 function getInputs(body) {
23541 if (body.length != 1) return;
23542 var lastExpression = body[0].expression;
23543 var candidate = lastExpression.toWatch;
23544 if (candidate.length !== 1) return candidate;
23545 return candidate[0] !== lastExpression ? candidate : undefined;
23548 function isAssignable(ast) {
23549 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
23552 function assignableAST(ast) {
23553 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
23554 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
23558 function isLiteral(ast) {
23559 return ast.body.length === 0 ||
23560 ast.body.length === 1 && (
23561 ast.body[0].expression.type === AST.Literal ||
23562 ast.body[0].expression.type === AST.ArrayExpression ||
23563 ast.body[0].expression.type === AST.ObjectExpression);
23566 function isConstant(ast) {
23567 return ast.constant;
23570 function ASTCompiler(astBuilder, $filter) {
23571 this.astBuilder = astBuilder;
23572 this.$filter = $filter;
23575 ASTCompiler.prototype = {
23576 compile: function(expression, expensiveChecks) {
23578 var ast = this.astBuilder.ast(expression);
23582 expensiveChecks: expensiveChecks,
23583 fn: {vars: [], body: [], own: {}},
23584 assign: {vars: [], body: [], own: {}},
23587 findConstantAndWatchExpressions(ast, self.$filter);
23590 this.stage = 'assign';
23591 if ((assignable = assignableAST(ast))) {
23592 this.state.computing = 'assign';
23593 var result = this.nextId();
23594 this.recurse(assignable, result);
23595 this.return_(result);
23596 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
23598 var toWatch = getInputs(ast.body);
23599 self.stage = 'inputs';
23600 forEach(toWatch, function(watch, key) {
23601 var fnKey = 'fn' + key;
23602 self.state[fnKey] = {vars: [], body: [], own: {}};
23603 self.state.computing = fnKey;
23604 var intoId = self.nextId();
23605 self.recurse(watch, intoId);
23606 self.return_(intoId);
23607 self.state.inputs.push(fnKey);
23608 watch.watchId = key;
23610 this.state.computing = 'fn';
23611 this.stage = 'main';
23614 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
23615 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
23616 '"' + this.USE + ' ' + this.STRICT + '";\n' +
23617 this.filterPrefix() +
23618 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
23624 var fn = (new Function('$filter',
23625 'ensureSafeMemberName',
23626 'ensureSafeObject',
23627 'ensureSafeFunction',
23629 'ensureSafeAssignContext',
23635 ensureSafeMemberName,
23637 ensureSafeFunction,
23639 ensureSafeAssignContext,
23644 this.state = this.stage = undefined;
23645 fn.literal = isLiteral(ast);
23646 fn.constant = isConstant(ast);
23654 watchFns: function() {
23656 var fns = this.state.inputs;
23658 forEach(fns, function(name) {
23659 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
23662 result.push('fn.inputs=[' + fns.join(',') + '];');
23664 return result.join('');
23667 generateFunction: function(name, params) {
23668 return 'function(' + params + '){' +
23669 this.varsPrefix(name) +
23674 filterPrefix: function() {
23677 forEach(this.state.filters, function(id, filter) {
23678 parts.push(id + '=$filter(' + self.escape(filter) + ')');
23680 if (parts.length) return 'var ' + parts.join(',') + ';';
23684 varsPrefix: function(section) {
23685 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
23688 body: function(section) {
23689 return this.state[section].body.join('');
23692 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
23693 var left, right, self = this, args, expression;
23694 recursionFn = recursionFn || noop;
23695 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
23696 intoId = intoId || this.nextId();
23698 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
23699 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
23703 switch (ast.type) {
23705 forEach(ast.body, function(expression, pos) {
23706 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
23707 if (pos !== ast.body.length - 1) {
23708 self.current().body.push(right, ';');
23710 self.return_(right);
23715 expression = this.escape(ast.value);
23716 this.assign(intoId, expression);
23717 recursionFn(expression);
23719 case AST.UnaryExpression:
23720 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
23721 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
23722 this.assign(intoId, expression);
23723 recursionFn(expression);
23725 case AST.BinaryExpression:
23726 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
23727 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
23728 if (ast.operator === '+') {
23729 expression = this.plus(left, right);
23730 } else if (ast.operator === '-') {
23731 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
23733 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
23735 this.assign(intoId, expression);
23736 recursionFn(expression);
23738 case AST.LogicalExpression:
23739 intoId = intoId || this.nextId();
23740 self.recurse(ast.left, intoId);
23741 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
23742 recursionFn(intoId);
23744 case AST.ConditionalExpression:
23745 intoId = intoId || this.nextId();
23746 self.recurse(ast.test, intoId);
23747 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
23748 recursionFn(intoId);
23750 case AST.Identifier:
23751 intoId = intoId || this.nextId();
23753 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
23754 nameId.computed = false;
23755 nameId.name = ast.name;
23757 ensureSafeMemberName(ast.name);
23758 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
23760 self.if_(self.stage === 'inputs' || 's', function() {
23761 if (create && create !== 1) {
23763 self.not(self.nonComputedMember('s', ast.name)),
23764 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
23766 self.assign(intoId, self.nonComputedMember('s', ast.name));
23768 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
23770 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
23771 self.addEnsureSafeObject(intoId);
23773 recursionFn(intoId);
23775 case AST.MemberExpression:
23776 left = nameId && (nameId.context = this.nextId()) || this.nextId();
23777 intoId = intoId || this.nextId();
23778 self.recurse(ast.object, left, undefined, function() {
23779 self.if_(self.notNull(left), function() {
23780 if (create && create !== 1) {
23781 self.addEnsureSafeAssignContext(left);
23783 if (ast.computed) {
23784 right = self.nextId();
23785 self.recurse(ast.property, right);
23786 self.getStringValue(right);
23787 self.addEnsureSafeMemberName(right);
23788 if (create && create !== 1) {
23789 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
23791 expression = self.ensureSafeObject(self.computedMember(left, right));
23792 self.assign(intoId, expression);
23794 nameId.computed = true;
23795 nameId.name = right;
23798 ensureSafeMemberName(ast.property.name);
23799 if (create && create !== 1) {
23800 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
23802 expression = self.nonComputedMember(left, ast.property.name);
23803 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
23804 expression = self.ensureSafeObject(expression);
23806 self.assign(intoId, expression);
23808 nameId.computed = false;
23809 nameId.name = ast.property.name;
23813 self.assign(intoId, 'undefined');
23815 recursionFn(intoId);
23818 case AST.CallExpression:
23819 intoId = intoId || this.nextId();
23821 right = self.filter(ast.callee.name);
23823 forEach(ast.arguments, function(expr) {
23824 var argument = self.nextId();
23825 self.recurse(expr, argument);
23826 args.push(argument);
23828 expression = right + '(' + args.join(',') + ')';
23829 self.assign(intoId, expression);
23830 recursionFn(intoId);
23832 right = self.nextId();
23835 self.recurse(ast.callee, right, left, function() {
23836 self.if_(self.notNull(right), function() {
23837 self.addEnsureSafeFunction(right);
23838 forEach(ast.arguments, function(expr) {
23839 self.recurse(expr, self.nextId(), undefined, function(argument) {
23840 args.push(self.ensureSafeObject(argument));
23844 if (!self.state.expensiveChecks) {
23845 self.addEnsureSafeObject(left.context);
23847 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
23849 expression = right + '(' + args.join(',') + ')';
23851 expression = self.ensureSafeObject(expression);
23852 self.assign(intoId, expression);
23854 self.assign(intoId, 'undefined');
23856 recursionFn(intoId);
23860 case AST.AssignmentExpression:
23861 right = this.nextId();
23863 if (!isAssignable(ast.left)) {
23864 throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
23866 this.recurse(ast.left, undefined, left, function() {
23867 self.if_(self.notNull(left.context), function() {
23868 self.recurse(ast.right, right);
23869 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
23870 self.addEnsureSafeAssignContext(left.context);
23871 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
23872 self.assign(intoId, expression);
23873 recursionFn(intoId || expression);
23877 case AST.ArrayExpression:
23879 forEach(ast.elements, function(expr) {
23880 self.recurse(expr, self.nextId(), undefined, function(argument) {
23881 args.push(argument);
23884 expression = '[' + args.join(',') + ']';
23885 this.assign(intoId, expression);
23886 recursionFn(expression);
23888 case AST.ObjectExpression:
23890 forEach(ast.properties, function(property) {
23891 self.recurse(property.value, self.nextId(), undefined, function(expr) {
23892 args.push(self.escape(
23893 property.key.type === AST.Identifier ? property.key.name :
23894 ('' + property.key.value)) +
23898 expression = '{' + args.join(',') + '}';
23899 this.assign(intoId, expression);
23900 recursionFn(expression);
23902 case AST.ThisExpression:
23903 this.assign(intoId, 's');
23906 case AST.LocalsExpression:
23907 this.assign(intoId, 'l');
23910 case AST.NGValueParameter:
23911 this.assign(intoId, 'v');
23917 getHasOwnProperty: function(element, property) {
23918 var key = element + '.' + property;
23919 var own = this.current().own;
23920 if (!own.hasOwnProperty(key)) {
23921 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
23926 assign: function(id, value) {
23928 this.current().body.push(id, '=', value, ';');
23932 filter: function(filterName) {
23933 if (!this.state.filters.hasOwnProperty(filterName)) {
23934 this.state.filters[filterName] = this.nextId(true);
23936 return this.state.filters[filterName];
23939 ifDefined: function(id, defaultValue) {
23940 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
23943 plus: function(left, right) {
23944 return 'plus(' + left + ',' + right + ')';
23947 return_: function(id) {
23948 this.current().body.push('return ', id, ';');
23951 if_: function(test, alternate, consequent) {
23952 if (test === true) {
23955 var body = this.current().body;
23956 body.push('if(', test, '){');
23960 body.push('else{');
23967 not: function(expression) {
23968 return '!(' + expression + ')';
23971 notNull: function(expression) {
23972 return expression + '!=null';
23975 nonComputedMember: function(left, right) {
23976 var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
23977 var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
23978 if (SAFE_IDENTIFIER.test(right)) {
23979 return left + '.' + right;
23981 return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
23985 computedMember: function(left, right) {
23986 return left + '[' + right + ']';
23989 member: function(left, right, computed) {
23990 if (computed) return this.computedMember(left, right);
23991 return this.nonComputedMember(left, right);
23994 addEnsureSafeObject: function(item) {
23995 this.current().body.push(this.ensureSafeObject(item), ';');
23998 addEnsureSafeMemberName: function(item) {
23999 this.current().body.push(this.ensureSafeMemberName(item), ';');
24002 addEnsureSafeFunction: function(item) {
24003 this.current().body.push(this.ensureSafeFunction(item), ';');
24006 addEnsureSafeAssignContext: function(item) {
24007 this.current().body.push(this.ensureSafeAssignContext(item), ';');
24010 ensureSafeObject: function(item) {
24011 return 'ensureSafeObject(' + item + ',text)';
24014 ensureSafeMemberName: function(item) {
24015 return 'ensureSafeMemberName(' + item + ',text)';
24018 ensureSafeFunction: function(item) {
24019 return 'ensureSafeFunction(' + item + ',text)';
24022 getStringValue: function(item) {
24023 this.assign(item, 'getStringValue(' + item + ')');
24026 ensureSafeAssignContext: function(item) {
24027 return 'ensureSafeAssignContext(' + item + ',text)';
24030 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
24032 return function() {
24033 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
24037 lazyAssign: function(id, value) {
24039 return function() {
24040 self.assign(id, value);
24044 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
24046 stringEscapeFn: function(c) {
24047 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
24050 escape: function(value) {
24051 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
24052 if (isNumber(value)) return value.toString();
24053 if (value === true) return 'true';
24054 if (value === false) return 'false';
24055 if (value === null) return 'null';
24056 if (typeof value === 'undefined') return 'undefined';
24058 throw $parseMinErr('esc', 'IMPOSSIBLE');
24061 nextId: function(skip, init) {
24062 var id = 'v' + (this.state.nextId++);
24064 this.current().vars.push(id + (init ? '=' + init : ''));
24069 current: function() {
24070 return this.state[this.state.computing];
24075 function ASTInterpreter(astBuilder, $filter) {
24076 this.astBuilder = astBuilder;
24077 this.$filter = $filter;
24080 ASTInterpreter.prototype = {
24081 compile: function(expression, expensiveChecks) {
24083 var ast = this.astBuilder.ast(expression);
24084 this.expression = expression;
24085 this.expensiveChecks = expensiveChecks;
24086 findConstantAndWatchExpressions(ast, self.$filter);
24089 if ((assignable = assignableAST(ast))) {
24090 assign = this.recurse(assignable);
24092 var toWatch = getInputs(ast.body);
24096 forEach(toWatch, function(watch, key) {
24097 var input = self.recurse(watch);
24098 watch.input = input;
24099 inputs.push(input);
24100 watch.watchId = key;
24103 var expressions = [];
24104 forEach(ast.body, function(expression) {
24105 expressions.push(self.recurse(expression.expression));
24107 var fn = ast.body.length === 0 ? noop :
24108 ast.body.length === 1 ? expressions[0] :
24109 function(scope, locals) {
24111 forEach(expressions, function(exp) {
24112 lastValue = exp(scope, locals);
24117 fn.assign = function(scope, value, locals) {
24118 return assign(scope, locals, value);
24122 fn.inputs = inputs;
24124 fn.literal = isLiteral(ast);
24125 fn.constant = isConstant(ast);
24129 recurse: function(ast, context, create) {
24130 var left, right, self = this, args, expression;
24132 return this.inputs(ast.input, ast.watchId);
24134 switch (ast.type) {
24136 return this.value(ast.value, context);
24137 case AST.UnaryExpression:
24138 right = this.recurse(ast.argument);
24139 return this['unary' + ast.operator](right, context);
24140 case AST.BinaryExpression:
24141 left = this.recurse(ast.left);
24142 right = this.recurse(ast.right);
24143 return this['binary' + ast.operator](left, right, context);
24144 case AST.LogicalExpression:
24145 left = this.recurse(ast.left);
24146 right = this.recurse(ast.right);
24147 return this['binary' + ast.operator](left, right, context);
24148 case AST.ConditionalExpression:
24149 return this['ternary?:'](
24150 this.recurse(ast.test),
24151 this.recurse(ast.alternate),
24152 this.recurse(ast.consequent),
24155 case AST.Identifier:
24156 ensureSafeMemberName(ast.name, self.expression);
24157 return self.identifier(ast.name,
24158 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
24159 context, create, self.expression);
24160 case AST.MemberExpression:
24161 left = this.recurse(ast.object, false, !!create);
24162 if (!ast.computed) {
24163 ensureSafeMemberName(ast.property.name, self.expression);
24164 right = ast.property.name;
24166 if (ast.computed) right = this.recurse(ast.property);
24167 return ast.computed ?
24168 this.computedMember(left, right, context, create, self.expression) :
24169 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
24170 case AST.CallExpression:
24172 forEach(ast.arguments, function(expr) {
24173 args.push(self.recurse(expr));
24175 if (ast.filter) right = this.$filter(ast.callee.name);
24176 if (!ast.filter) right = this.recurse(ast.callee, true);
24177 return ast.filter ?
24178 function(scope, locals, assign, inputs) {
24180 for (var i = 0; i < args.length; ++i) {
24181 values.push(args[i](scope, locals, assign, inputs));
24183 var value = right.apply(undefined, values, inputs);
24184 return context ? {context: undefined, name: undefined, value: value} : value;
24186 function(scope, locals, assign, inputs) {
24187 var rhs = right(scope, locals, assign, inputs);
24189 if (rhs.value != null) {
24190 ensureSafeObject(rhs.context, self.expression);
24191 ensureSafeFunction(rhs.value, self.expression);
24193 for (var i = 0; i < args.length; ++i) {
24194 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
24196 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
24198 return context ? {value: value} : value;
24200 case AST.AssignmentExpression:
24201 left = this.recurse(ast.left, true, 1);
24202 right = this.recurse(ast.right);
24203 return function(scope, locals, assign, inputs) {
24204 var lhs = left(scope, locals, assign, inputs);
24205 var rhs = right(scope, locals, assign, inputs);
24206 ensureSafeObject(lhs.value, self.expression);
24207 ensureSafeAssignContext(lhs.context);
24208 lhs.context[lhs.name] = rhs;
24209 return context ? {value: rhs} : rhs;
24211 case AST.ArrayExpression:
24213 forEach(ast.elements, function(expr) {
24214 args.push(self.recurse(expr));
24216 return function(scope, locals, assign, inputs) {
24218 for (var i = 0; i < args.length; ++i) {
24219 value.push(args[i](scope, locals, assign, inputs));
24221 return context ? {value: value} : value;
24223 case AST.ObjectExpression:
24225 forEach(ast.properties, function(property) {
24226 args.push({key: property.key.type === AST.Identifier ?
24227 property.key.name :
24228 ('' + property.key.value),
24229 value: self.recurse(property.value)
24232 return function(scope, locals, assign, inputs) {
24234 for (var i = 0; i < args.length; ++i) {
24235 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
24237 return context ? {value: value} : value;
24239 case AST.ThisExpression:
24240 return function(scope) {
24241 return context ? {value: scope} : scope;
24243 case AST.LocalsExpression:
24244 return function(scope, locals) {
24245 return context ? {value: locals} : locals;
24247 case AST.NGValueParameter:
24248 return function(scope, locals, assign) {
24249 return context ? {value: assign} : assign;
24254 'unary+': function(argument, context) {
24255 return function(scope, locals, assign, inputs) {
24256 var arg = argument(scope, locals, assign, inputs);
24257 if (isDefined(arg)) {
24262 return context ? {value: arg} : arg;
24265 'unary-': function(argument, context) {
24266 return function(scope, locals, assign, inputs) {
24267 var arg = argument(scope, locals, assign, inputs);
24268 if (isDefined(arg)) {
24273 return context ? {value: arg} : arg;
24276 'unary!': function(argument, context) {
24277 return function(scope, locals, assign, inputs) {
24278 var arg = !argument(scope, locals, assign, inputs);
24279 return context ? {value: arg} : arg;
24282 'binary+': function(left, right, context) {
24283 return function(scope, locals, assign, inputs) {
24284 var lhs = left(scope, locals, assign, inputs);
24285 var rhs = right(scope, locals, assign, inputs);
24286 var arg = plusFn(lhs, rhs);
24287 return context ? {value: arg} : arg;
24290 'binary-': function(left, right, context) {
24291 return function(scope, locals, assign, inputs) {
24292 var lhs = left(scope, locals, assign, inputs);
24293 var rhs = right(scope, locals, assign, inputs);
24294 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
24295 return context ? {value: arg} : arg;
24298 'binary*': function(left, right, context) {
24299 return function(scope, locals, assign, inputs) {
24300 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
24301 return context ? {value: arg} : arg;
24304 'binary/': function(left, right, context) {
24305 return function(scope, locals, assign, inputs) {
24306 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
24307 return context ? {value: arg} : arg;
24310 'binary%': function(left, right, context) {
24311 return function(scope, locals, assign, inputs) {
24312 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
24313 return context ? {value: arg} : arg;
24316 'binary===': function(left, right, context) {
24317 return function(scope, locals, assign, inputs) {
24318 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
24319 return context ? {value: arg} : arg;
24322 'binary!==': function(left, right, context) {
24323 return function(scope, locals, assign, inputs) {
24324 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
24325 return context ? {value: arg} : arg;
24328 'binary==': function(left, right, context) {
24329 return function(scope, locals, assign, inputs) {
24330 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
24331 return context ? {value: arg} : arg;
24334 'binary!=': function(left, right, context) {
24335 return function(scope, locals, assign, inputs) {
24336 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
24337 return context ? {value: arg} : arg;
24340 'binary<': function(left, right, context) {
24341 return function(scope, locals, assign, inputs) {
24342 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
24343 return context ? {value: arg} : arg;
24346 'binary>': function(left, right, context) {
24347 return function(scope, locals, assign, inputs) {
24348 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
24349 return context ? {value: arg} : arg;
24352 'binary<=': function(left, right, context) {
24353 return function(scope, locals, assign, inputs) {
24354 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
24355 return context ? {value: arg} : arg;
24358 'binary>=': function(left, right, context) {
24359 return function(scope, locals, assign, inputs) {
24360 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
24361 return context ? {value: arg} : arg;
24364 'binary&&': function(left, right, context) {
24365 return function(scope, locals, assign, inputs) {
24366 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
24367 return context ? {value: arg} : arg;
24370 'binary||': function(left, right, context) {
24371 return function(scope, locals, assign, inputs) {
24372 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
24373 return context ? {value: arg} : arg;
24376 'ternary?:': function(test, alternate, consequent, context) {
24377 return function(scope, locals, assign, inputs) {
24378 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
24379 return context ? {value: arg} : arg;
24382 value: function(value, context) {
24383 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
24385 identifier: function(name, expensiveChecks, context, create, expression) {
24386 return function(scope, locals, assign, inputs) {
24387 var base = locals && (name in locals) ? locals : scope;
24388 if (create && create !== 1 && base && !(base[name])) {
24391 var value = base ? base[name] : undefined;
24392 if (expensiveChecks) {
24393 ensureSafeObject(value, expression);
24396 return {context: base, name: name, value: value};
24402 computedMember: function(left, right, context, create, expression) {
24403 return function(scope, locals, assign, inputs) {
24404 var lhs = left(scope, locals, assign, inputs);
24408 rhs = right(scope, locals, assign, inputs);
24409 rhs = getStringValue(rhs);
24410 ensureSafeMemberName(rhs, expression);
24411 if (create && create !== 1) {
24412 ensureSafeAssignContext(lhs);
24413 if (lhs && !(lhs[rhs])) {
24418 ensureSafeObject(value, expression);
24421 return {context: lhs, name: rhs, value: value};
24427 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
24428 return function(scope, locals, assign, inputs) {
24429 var lhs = left(scope, locals, assign, inputs);
24430 if (create && create !== 1) {
24431 ensureSafeAssignContext(lhs);
24432 if (lhs && !(lhs[right])) {
24436 var value = lhs != null ? lhs[right] : undefined;
24437 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
24438 ensureSafeObject(value, expression);
24441 return {context: lhs, name: right, value: value};
24447 inputs: function(input, watchId) {
24448 return function(scope, value, locals, inputs) {
24449 if (inputs) return inputs[watchId];
24450 return input(scope, value, locals);
24458 var Parser = function(lexer, $filter, options) {
24459 this.lexer = lexer;
24460 this.$filter = $filter;
24461 this.options = options;
24462 this.ast = new AST(lexer, options);
24463 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
24464 new ASTCompiler(this.ast, $filter);
24467 Parser.prototype = {
24468 constructor: Parser,
24470 parse: function(text) {
24471 return this.astCompiler.compile(text, this.options.expensiveChecks);
24475 function isPossiblyDangerousMemberName(name) {
24476 return name == 'constructor';
24479 var objectValueOf = Object.prototype.valueOf;
24481 function getValueOf(value) {
24482 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
24485 ///////////////////////////////////
24494 * Converts Angular {@link guide/expression expression} into a function.
24497 * var getter = $parse('user.name');
24498 * var setter = getter.assign;
24499 * var context = {user:{name:'angular'}};
24500 * var locals = {user:{name:'local'}};
24502 * expect(getter(context)).toEqual('angular');
24503 * setter(context, 'newValue');
24504 * expect(context.user.name).toEqual('newValue');
24505 * expect(getter(context, locals)).toEqual('local');
24509 * @param {string} expression String expression to compile.
24510 * @returns {function(context, locals)} a function which represents the compiled expression:
24512 * * `context` – `{object}` – an object against which any expressions embedded in the strings
24513 * are evaluated against (typically a scope object).
24514 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
24517 * The returned function also has the following properties:
24518 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
24520 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
24521 * constant literals.
24522 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
24523 * set to a function to change its value on the given context.
24530 * @name $parseProvider
24533 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
24536 function $ParseProvider() {
24537 var cacheDefault = createMap();
24538 var cacheExpensive = createMap();
24543 'undefined': undefined
24545 var identStart, identContinue;
24549 * @name $parseProvider#addLiteral
24552 * Configure $parse service to add literal values that will be present as literal at expressions.
24554 * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
24555 * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
24558 this.addLiteral = function(literalName, literalValue) {
24559 literals[literalName] = literalValue;
24564 * @name $parseProvider#setIdentifierFns
24567 * Allows defining the set of characters that are allowed in Angular expressions. The function
24568 * `identifierStart` will get called to know if a given character is a valid character to be the
24569 * first character for an identifier. The function `identifierContinue` will get called to know if
24570 * a given character is a valid character to be a follow-up identifier character. The functions
24571 * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
24572 * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
24573 * mind that the `string` parameter can be two characters long depending on the character
24574 * representation. It is expected for the function to return `true` or `false`, whether that
24575 * character is allowed or not.
24577 * Since this function will be called extensivelly, keep the implementation of these functions fast,
24578 * as the performance of these functions have a direct impact on the expressions parsing speed.
24580 * @param {function=} identifierStart The function that will decide whether the given character is
24581 * a valid identifier start character.
24582 * @param {function=} identifierContinue The function that will decide whether the given character is
24583 * a valid identifier continue character.
24585 this.setIdentifierFns = function(identifierStart, identifierContinue) {
24586 identStart = identifierStart;
24587 identContinue = identifierContinue;
24591 this.$get = ['$filter', function($filter) {
24592 var noUnsafeEval = csp().noUnsafeEval;
24593 var $parseOptions = {
24595 expensiveChecks: false,
24596 literals: copy(literals),
24597 isIdentifierStart: isFunction(identStart) && identStart,
24598 isIdentifierContinue: isFunction(identContinue) && identContinue
24600 $parseOptionsExpensive = {
24602 expensiveChecks: true,
24603 literals: copy(literals),
24604 isIdentifierStart: isFunction(identStart) && identStart,
24605 isIdentifierContinue: isFunction(identContinue) && identContinue
24607 var runningChecksEnabled = false;
24609 $parse.$$runningExpensiveChecks = function() {
24610 return runningChecksEnabled;
24615 function $parse(exp, interceptorFn, expensiveChecks) {
24616 var parsedExpression, oneTime, cacheKey;
24618 expensiveChecks = expensiveChecks || runningChecksEnabled;
24620 switch (typeof exp) {
24625 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
24626 parsedExpression = cache[cacheKey];
24628 if (!parsedExpression) {
24629 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
24631 exp = exp.substring(2);
24633 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
24634 var lexer = new Lexer(parseOptions);
24635 var parser = new Parser(lexer, $filter, parseOptions);
24636 parsedExpression = parser.parse(exp);
24637 if (parsedExpression.constant) {
24638 parsedExpression.$$watchDelegate = constantWatchDelegate;
24639 } else if (oneTime) {
24640 parsedExpression.$$watchDelegate = parsedExpression.literal ?
24641 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
24642 } else if (parsedExpression.inputs) {
24643 parsedExpression.$$watchDelegate = inputsWatchDelegate;
24645 if (expensiveChecks) {
24646 parsedExpression = expensiveChecksInterceptor(parsedExpression);
24648 cache[cacheKey] = parsedExpression;
24650 return addInterceptor(parsedExpression, interceptorFn);
24653 return addInterceptor(exp, interceptorFn);
24656 return addInterceptor(noop, interceptorFn);
24660 function expensiveChecksInterceptor(fn) {
24661 if (!fn) return fn;
24662 expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
24663 expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
24664 expensiveCheckFn.constant = fn.constant;
24665 expensiveCheckFn.literal = fn.literal;
24666 for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
24667 fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
24669 expensiveCheckFn.inputs = fn.inputs;
24671 return expensiveCheckFn;
24673 function expensiveCheckFn(scope, locals, assign, inputs) {
24674 var expensiveCheckOldValue = runningChecksEnabled;
24675 runningChecksEnabled = true;
24677 return fn(scope, locals, assign, inputs);
24679 runningChecksEnabled = expensiveCheckOldValue;
24684 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
24686 if (newValue == null || oldValueOfValue == null) { // null/undefined
24687 return newValue === oldValueOfValue;
24690 if (typeof newValue === 'object') {
24692 // attempt to convert the value to a primitive type
24693 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
24694 // be cheaply dirty-checked
24695 newValue = getValueOf(newValue);
24697 if (typeof newValue === 'object') {
24698 // objects/arrays are not supported - deep-watching them would be too expensive
24702 // fall-through to the primitive equality check
24706 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
24709 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
24710 var inputExpressions = parsedExpression.inputs;
24713 if (inputExpressions.length === 1) {
24714 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
24715 inputExpressions = inputExpressions[0];
24716 return scope.$watch(function expressionInputWatch(scope) {
24717 var newInputValue = inputExpressions(scope);
24718 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
24719 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
24720 oldInputValueOf = newInputValue && getValueOf(newInputValue);
24723 }, listener, objectEquality, prettyPrintExpression);
24726 var oldInputValueOfValues = [];
24727 var oldInputValues = [];
24728 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
24729 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
24730 oldInputValues[i] = null;
24733 return scope.$watch(function expressionInputsWatch(scope) {
24734 var changed = false;
24736 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
24737 var newInputValue = inputExpressions[i](scope);
24738 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
24739 oldInputValues[i] = newInputValue;
24740 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
24745 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
24749 }, listener, objectEquality, prettyPrintExpression);
24752 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
24753 var unwatch, lastValue;
24754 return unwatch = scope.$watch(function oneTimeWatch(scope) {
24755 return parsedExpression(scope);
24756 }, function oneTimeListener(value, old, scope) {
24758 if (isFunction(listener)) {
24759 listener.apply(this, arguments);
24761 if (isDefined(value)) {
24762 scope.$$postDigest(function() {
24763 if (isDefined(lastValue)) {
24768 }, objectEquality);
24771 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
24772 var unwatch, lastValue;
24773 return unwatch = scope.$watch(function oneTimeWatch(scope) {
24774 return parsedExpression(scope);
24775 }, function oneTimeListener(value, old, scope) {
24777 if (isFunction(listener)) {
24778 listener.call(this, value, old, scope);
24780 if (isAllDefined(value)) {
24781 scope.$$postDigest(function() {
24782 if (isAllDefined(lastValue)) unwatch();
24785 }, objectEquality);
24787 function isAllDefined(value) {
24788 var allDefined = true;
24789 forEach(value, function(val) {
24790 if (!isDefined(val)) allDefined = false;
24796 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
24798 return unwatch = scope.$watch(function constantWatch(scope) {
24800 return parsedExpression(scope);
24801 }, listener, objectEquality);
24804 function addInterceptor(parsedExpression, interceptorFn) {
24805 if (!interceptorFn) return parsedExpression;
24806 var watchDelegate = parsedExpression.$$watchDelegate;
24807 var useInputs = false;
24810 watchDelegate !== oneTimeLiteralWatchDelegate &&
24811 watchDelegate !== oneTimeWatchDelegate;
24813 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
24814 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
24815 return interceptorFn(value, scope, locals);
24816 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
24817 var value = parsedExpression(scope, locals, assign, inputs);
24818 var result = interceptorFn(value, scope, locals);
24819 // we only return the interceptor's result if the
24820 // initial value is defined (for bind-once)
24821 return isDefined(value) ? result : value;
24824 // Propagate $$watchDelegates other then inputsWatchDelegate
24825 if (parsedExpression.$$watchDelegate &&
24826 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
24827 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
24828 } else if (!interceptorFn.$stateful) {
24829 // If there is an interceptor, but no watchDelegate then treat the interceptor like
24830 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
24831 fn.$$watchDelegate = inputsWatchDelegate;
24832 useInputs = !parsedExpression.inputs;
24833 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
24844 * @requires $rootScope
24847 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
24848 * when they are done processing.
24850 * This is an implementation of promises/deferred objects inspired by
24851 * [Kris Kowal's Q](https://github.com/kriskowal/q).
24853 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
24854 * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
24858 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
24859 * function as the first argument. This is similar to the native Promise implementation from ES6,
24860 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
24862 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
24865 * It can be used like so:
24868 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
24869 * // are available in the current lexical scope (they could have been injected or passed in).
24871 * function asyncGreet(name) {
24872 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
24873 * return $q(function(resolve, reject) {
24874 * setTimeout(function() {
24875 * if (okToGreet(name)) {
24876 * resolve('Hello, ' + name + '!');
24878 * reject('Greeting ' + name + ' is not allowed.');
24884 * var promise = asyncGreet('Robin Hood');
24885 * promise.then(function(greeting) {
24886 * alert('Success: ' + greeting);
24887 * }, function(reason) {
24888 * alert('Failed: ' + reason);
24892 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
24894 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
24896 * However, the more traditional CommonJS-style usage is still available, and documented below.
24898 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
24899 * interface for interacting with an object that represents the result of an action that is
24900 * performed asynchronously, and may or may not be finished at any given point in time.
24902 * From the perspective of dealing with error handling, deferred and promise APIs are to
24903 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
24906 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
24907 * // are available in the current lexical scope (they could have been injected or passed in).
24909 * function asyncGreet(name) {
24910 * var deferred = $q.defer();
24912 * setTimeout(function() {
24913 * deferred.notify('About to greet ' + name + '.');
24915 * if (okToGreet(name)) {
24916 * deferred.resolve('Hello, ' + name + '!');
24918 * deferred.reject('Greeting ' + name + ' is not allowed.');
24922 * return deferred.promise;
24925 * var promise = asyncGreet('Robin Hood');
24926 * promise.then(function(greeting) {
24927 * alert('Success: ' + greeting);
24928 * }, function(reason) {
24929 * alert('Failed: ' + reason);
24930 * }, function(update) {
24931 * alert('Got notification: ' + update);
24935 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
24936 * comes in the way of guarantees that promise and deferred APIs make, see
24937 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
24939 * Additionally the promise api allows for composition that is very hard to do with the
24940 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
24941 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
24942 * section on serial or parallel joining of promises.
24944 * # The Deferred API
24946 * A new instance of deferred is constructed by calling `$q.defer()`.
24948 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
24949 * that can be used for signaling the successful or unsuccessful completion, as well as the status
24954 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
24955 * constructed via `$q.reject`, the promise will be rejected instead.
24956 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
24957 * resolving it with a rejection constructed via `$q.reject`.
24958 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
24959 * multiple times before the promise is either resolved or rejected.
24963 * - promise – `{Promise}` – promise object associated with this deferred.
24966 * # The Promise API
24968 * A new promise instance is created when a deferred instance is created and can be retrieved by
24969 * calling `deferred.promise`.
24971 * The purpose of the promise object is to allow for interested parties to get access to the result
24972 * of the deferred task when it completes.
24976 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
24977 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
24978 * as soon as the result is available. The callbacks are called with a single argument: the result
24979 * or rejection reason. Additionally, the notify callback may be called zero or more times to
24980 * provide a progress indication, before the promise is resolved or rejected.
24982 * This method *returns a new promise* which is resolved or rejected via the return value of the
24983 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
24984 * with the value which is resolved in that promise using
24985 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
24986 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
24987 * resolved or rejected from the notifyCallback method.
24989 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
24991 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
24992 * but to do so without modifying the final value. This is useful to release resources or do some
24993 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
24994 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
24995 * more information.
24997 * # Chaining promises
24999 * Because calling the `then` method of a promise returns a new derived promise, it is easily
25000 * possible to create a chain of promises:
25003 * promiseB = promiseA.then(function(result) {
25004 * return result + 1;
25007 * // promiseB will be resolved immediately after promiseA is resolved and its value
25008 * // will be the result of promiseA incremented by 1
25011 * It is possible to create chains of any length and since a promise can be resolved with another
25012 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
25013 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
25014 * $http's response interceptors.
25017 * # Differences between Kris Kowal's Q and $q
25019 * There are two main differences:
25021 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
25022 * mechanism in angular, which means faster propagation of resolution or rejection into your
25023 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
25024 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
25025 * all the important functionality needed for common async tasks.
25030 * it('should simulate promise', inject(function($q, $rootScope) {
25031 * var deferred = $q.defer();
25032 * var promise = deferred.promise;
25033 * var resolvedValue;
25035 * promise.then(function(value) { resolvedValue = value; });
25036 * expect(resolvedValue).toBeUndefined();
25038 * // Simulate resolving of promise
25039 * deferred.resolve(123);
25040 * // Note that the 'then' function does not get called synchronously.
25041 * // This is because we want the promise API to always be async, whether or not
25042 * // it got called synchronously or asynchronously.
25043 * expect(resolvedValue).toBeUndefined();
25045 * // Propagate promise resolution to 'then' functions using $apply().
25046 * $rootScope.$apply();
25047 * expect(resolvedValue).toEqual(123);
25051 * @param {function(function, function)} resolver Function which is responsible for resolving or
25052 * rejecting the newly created promise. The first parameter is a function which resolves the
25053 * promise, the second parameter is a function which rejects the promise.
25055 * @returns {Promise} The newly created promise.
25057 function $QProvider() {
25059 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
25060 return qFactory(function(callback) {
25061 $rootScope.$evalAsync(callback);
25062 }, $exceptionHandler);
25066 function $$QProvider() {
25067 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
25068 return qFactory(function(callback) {
25069 $browser.defer(callback);
25070 }, $exceptionHandler);
25075 * Constructs a promise manager.
25077 * @param {function(function)} nextTick Function for executing functions in the next turn.
25078 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
25079 * debugging purposes.
25080 * @returns {object} Promise manager.
25082 function qFactory(nextTick, exceptionHandler) {
25083 var $qMinErr = minErr('$q', TypeError);
25087 * @name ng.$q#defer
25091 * Creates a `Deferred` object which represents a task which will finish in the future.
25093 * @returns {Deferred} Returns a new instance of deferred.
25095 var defer = function() {
25096 var d = new Deferred();
25097 //Necessary to support unbound execution :/
25098 d.resolve = simpleBind(d, d.resolve);
25099 d.reject = simpleBind(d, d.reject);
25100 d.notify = simpleBind(d, d.notify);
25104 function Promise() {
25105 this.$$state = { status: 0 };
25108 extend(Promise.prototype, {
25109 then: function(onFulfilled, onRejected, progressBack) {
25110 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
25113 var result = new Deferred();
25115 this.$$state.pending = this.$$state.pending || [];
25116 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
25117 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
25119 return result.promise;
25122 "catch": function(callback) {
25123 return this.then(null, callback);
25126 "finally": function(callback, progressBack) {
25127 return this.then(function(value) {
25128 return handleCallback(value, true, callback);
25129 }, function(error) {
25130 return handleCallback(error, false, callback);
25135 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
25136 function simpleBind(context, fn) {
25137 return function(value) {
25138 fn.call(context, value);
25142 function processQueue(state) {
25143 var fn, deferred, pending;
25145 pending = state.pending;
25146 state.processScheduled = false;
25147 state.pending = undefined;
25148 for (var i = 0, ii = pending.length; i < ii; ++i) {
25149 deferred = pending[i][0];
25150 fn = pending[i][state.status];
25152 if (isFunction(fn)) {
25153 deferred.resolve(fn(state.value));
25154 } else if (state.status === 1) {
25155 deferred.resolve(state.value);
25157 deferred.reject(state.value);
25160 deferred.reject(e);
25161 exceptionHandler(e);
25166 function scheduleProcessQueue(state) {
25167 if (state.processScheduled || !state.pending) return;
25168 state.processScheduled = true;
25169 nextTick(function() { processQueue(state); });
25172 function Deferred() {
25173 this.promise = new Promise();
25176 extend(Deferred.prototype, {
25177 resolve: function(val) {
25178 if (this.promise.$$state.status) return;
25179 if (val === this.promise) {
25180 this.$$reject($qMinErr(
25182 "Expected promise to be resolved with value other than itself '{0}'",
25185 this.$$resolve(val);
25190 $$resolve: function(val) {
25195 if ((isObject(val) || isFunction(val))) then = val && val.then;
25196 if (isFunction(then)) {
25197 this.promise.$$state.status = -1;
25198 then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
25200 this.promise.$$state.value = val;
25201 this.promise.$$state.status = 1;
25202 scheduleProcessQueue(this.promise.$$state);
25206 exceptionHandler(e);
25209 function resolvePromise(val) {
25212 that.$$resolve(val);
25214 function rejectPromise(val) {
25217 that.$$reject(val);
25221 reject: function(reason) {
25222 if (this.promise.$$state.status) return;
25223 this.$$reject(reason);
25226 $$reject: function(reason) {
25227 this.promise.$$state.value = reason;
25228 this.promise.$$state.status = 2;
25229 scheduleProcessQueue(this.promise.$$state);
25232 notify: function(progress) {
25233 var callbacks = this.promise.$$state.pending;
25235 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
25236 nextTick(function() {
25237 var callback, result;
25238 for (var i = 0, ii = callbacks.length; i < ii; i++) {
25239 result = callbacks[i][0];
25240 callback = callbacks[i][3];
25242 result.notify(isFunction(callback) ? callback(progress) : progress);
25244 exceptionHandler(e);
25258 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
25259 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
25260 * a promise chain, you don't need to worry about it.
25262 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
25263 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
25264 * a promise error callback and you want to forward the error to the promise derived from the
25265 * current promise, you have to "rethrow" the error by returning a rejection constructed via
25269 * promiseB = promiseA.then(function(result) {
25270 * // success: do something and resolve promiseB
25271 * // with the old or a new result
25273 * }, function(reason) {
25274 * // error: handle the error if possible and
25275 * // resolve promiseB with newPromiseOrValue,
25276 * // otherwise forward the rejection to promiseB
25277 * if (canHandle(reason)) {
25278 * // handle the error and recover
25279 * return newPromiseOrValue;
25281 * return $q.reject(reason);
25285 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
25286 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
25288 var reject = function(reason) {
25289 var result = new Deferred();
25290 result.reject(reason);
25291 return result.promise;
25294 var makePromise = function makePromise(value, resolved) {
25295 var result = new Deferred();
25297 result.resolve(value);
25299 result.reject(value);
25301 return result.promise;
25304 var handleCallback = function handleCallback(value, isResolved, callback) {
25305 var callbackOutput = null;
25307 if (isFunction(callback)) callbackOutput = callback();
25309 return makePromise(e, false);
25311 if (isPromiseLike(callbackOutput)) {
25312 return callbackOutput.then(function() {
25313 return makePromise(value, isResolved);
25314 }, function(error) {
25315 return makePromise(error, false);
25318 return makePromise(value, isResolved);
25328 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
25329 * This is useful when you are dealing with an object that might or might not be a promise, or if
25330 * the promise comes from a source that can't be trusted.
25332 * @param {*} value Value or a promise
25333 * @param {Function=} successCallback
25334 * @param {Function=} errorCallback
25335 * @param {Function=} progressCallback
25336 * @returns {Promise} Returns a promise of the passed value or promise
25340 var when = function(value, callback, errback, progressBack) {
25341 var result = new Deferred();
25342 result.resolve(value);
25343 return result.promise.then(callback, errback, progressBack);
25352 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
25354 * @param {*} value Value or a promise
25355 * @param {Function=} successCallback
25356 * @param {Function=} errorCallback
25357 * @param {Function=} progressCallback
25358 * @returns {Promise} Returns a promise of the passed value or promise
25360 var resolve = when;
25368 * Combines multiple promises into a single promise that is resolved when all of the input
25369 * promises are resolved.
25371 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
25372 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
25373 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
25374 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
25375 * with the same rejection value.
25378 function all(promises) {
25379 var deferred = new Deferred(),
25381 results = isArray(promises) ? [] : {};
25383 forEach(promises, function(promise, key) {
25385 when(promise).then(function(value) {
25386 if (results.hasOwnProperty(key)) return;
25387 results[key] = value;
25388 if (!(--counter)) deferred.resolve(results);
25389 }, function(reason) {
25390 if (results.hasOwnProperty(key)) return;
25391 deferred.reject(reason);
25395 if (counter === 0) {
25396 deferred.resolve(results);
25399 return deferred.promise;
25402 var $Q = function Q(resolver) {
25403 if (!isFunction(resolver)) {
25404 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
25407 var deferred = new Deferred();
25409 function resolveFn(value) {
25410 deferred.resolve(value);
25413 function rejectFn(reason) {
25414 deferred.reject(reason);
25417 resolver(resolveFn, rejectFn);
25419 return deferred.promise;
25422 // Let's make the instanceof operator work for promises, so that
25423 // `new $q(fn) instanceof $q` would evaluate to true.
25424 $Q.prototype = Promise.prototype;
25427 $Q.reject = reject;
25429 $Q.resolve = resolve;
25435 function $$RAFProvider() { //rAF
25436 this.$get = ['$window', '$timeout', function($window, $timeout) {
25437 var requestAnimationFrame = $window.requestAnimationFrame ||
25438 $window.webkitRequestAnimationFrame;
25440 var cancelAnimationFrame = $window.cancelAnimationFrame ||
25441 $window.webkitCancelAnimationFrame ||
25442 $window.webkitCancelRequestAnimationFrame;
25444 var rafSupported = !!requestAnimationFrame;
25445 var raf = rafSupported
25447 var id = requestAnimationFrame(fn);
25448 return function() {
25449 cancelAnimationFrame(id);
25453 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
25454 return function() {
25455 $timeout.cancel(timer);
25459 raf.supported = rafSupported;
25468 * The design decisions behind the scope are heavily favored for speed and memory consumption.
25470 * The typical use of scope is to watch the expressions, which most of the time return the same
25471 * value as last time so we optimize the operation.
25473 * Closures construction is expensive in terms of speed as well as memory:
25474 * - No closures, instead use prototypical inheritance for API
25475 * - Internal state needs to be stored on scope directly, which means that private state is
25476 * exposed as $$____ properties
25478 * Loop operations are optimized by using while(count--) { ... }
25479 * - This means that in order to keep the same order of execution as addition we have to add
25480 * items to the array at the beginning (unshift) instead of at the end (push)
25482 * Child scopes are created and removed often
25483 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
25485 * There are fewer watches than observers. This is why you don't want the observer to be implemented
25486 * in the same way as watch. Watch requires return of the initialization function which is expensive
25493 * @name $rootScopeProvider
25496 * Provider for the $rootScope service.
25501 * @name $rootScopeProvider#digestTtl
25504 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
25505 * assuming that the model is unstable.
25507 * The current default is 10 iterations.
25509 * In complex applications it's possible that the dependencies between `$watch`s will result in
25510 * several digest iterations. However if an application needs more than the default 10 digest
25511 * iterations for its model to stabilize then you should investigate what is causing the model to
25512 * continuously change during the digest.
25514 * Increasing the TTL could have performance implications, so you should not change it without
25515 * proper justification.
25517 * @param {number} limit The number of digest iterations.
25526 * Every application has a single root {@link ng.$rootScope.Scope scope}.
25527 * All other scopes are descendant scopes of the root scope. Scopes provide separation
25528 * between the model and the view, via a mechanism for watching the model for changes.
25529 * They also provide event emission/broadcast and subscription facility. See the
25530 * {@link guide/scope developer guide on scopes}.
25532 function $RootScopeProvider() {
25534 var $rootScopeMinErr = minErr('$rootScope');
25535 var lastDirtyWatch = null;
25536 var applyAsyncId = null;
25538 this.digestTtl = function(value) {
25539 if (arguments.length) {
25545 function createChildScopeClass(parent) {
25546 function ChildScope() {
25547 this.$$watchers = this.$$nextSibling =
25548 this.$$childHead = this.$$childTail = null;
25549 this.$$listeners = {};
25550 this.$$listenerCount = {};
25551 this.$$watchersCount = 0;
25552 this.$id = nextUid();
25553 this.$$ChildScope = null;
25555 ChildScope.prototype = parent;
25559 this.$get = ['$exceptionHandler', '$parse', '$browser',
25560 function($exceptionHandler, $parse, $browser) {
25562 function destroyChildScope($event) {
25563 $event.currentScope.$$destroyed = true;
25566 function cleanUpScope($scope) {
25569 // There is a memory leak in IE9 if all child scopes are not disconnected
25570 // completely when a scope is destroyed. So this code will recurse up through
25571 // all this scopes children
25573 // See issue https://github.com/angular/angular.js/issues/10706
25574 $scope.$$childHead && cleanUpScope($scope.$$childHead);
25575 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
25578 // The code below works around IE9 and V8's memory leaks
25581 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
25582 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
25583 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
25585 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
25586 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
25591 * @name $rootScope.Scope
25594 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
25595 * {@link auto.$injector $injector}. Child scopes are created using the
25596 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
25597 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
25598 * an in-depth introduction and usage examples.
25602 * A scope can inherit from a parent scope, as in this example:
25604 var parent = $rootScope;
25605 var child = parent.$new();
25607 parent.salutation = "Hello";
25608 expect(child.salutation).toEqual('Hello');
25610 child.salutation = "Welcome";
25611 expect(child.salutation).toEqual('Welcome');
25612 expect(parent.salutation).toEqual('Hello');
25615 * When interacting with `Scope` in tests, additional helper methods are available on the
25616 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
25620 * @param {Object.<string, function()>=} providers Map of service factory which need to be
25621 * provided for the current scope. Defaults to {@link ng}.
25622 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
25623 * append/override services provided by `providers`. This is handy
25624 * when unit-testing and having the need to override a default
25626 * @returns {Object} Newly created scope.
25630 this.$id = nextUid();
25631 this.$$phase = this.$parent = this.$$watchers =
25632 this.$$nextSibling = this.$$prevSibling =
25633 this.$$childHead = this.$$childTail = null;
25635 this.$$destroyed = false;
25636 this.$$listeners = {};
25637 this.$$listenerCount = {};
25638 this.$$watchersCount = 0;
25639 this.$$isolateBindings = null;
25644 * @name $rootScope.Scope#$id
25647 * Unique scope ID (monotonically increasing) useful for debugging.
25652 * @name $rootScope.Scope#$parent
25655 * Reference to the parent scope.
25660 * @name $rootScope.Scope#$root
25663 * Reference to the root scope.
25666 Scope.prototype = {
25667 constructor: Scope,
25670 * @name $rootScope.Scope#$new
25674 * Creates a new child {@link ng.$rootScope.Scope scope}.
25676 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
25677 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
25679 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
25680 * desired for the scope and its child scopes to be permanently detached from the parent and
25681 * thus stop participating in model change detection and listener notification by invoking.
25683 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
25684 * parent scope. The scope is isolated, as it can not see parent scope properties.
25685 * When creating widgets, it is useful for the widget to not accidentally read parent
25688 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
25689 * of the newly created scope. Defaults to `this` scope if not provided.
25690 * This is used when creating a transclude scope to correctly place it
25691 * in the scope hierarchy while maintaining the correct prototypical
25694 * @returns {Object} The newly created child scope.
25697 $new: function(isolate, parent) {
25700 parent = parent || this;
25703 child = new Scope();
25704 child.$root = this.$root;
25706 // Only create a child scope class if somebody asks for one,
25707 // but cache it to allow the VM to optimize lookups.
25708 if (!this.$$ChildScope) {
25709 this.$$ChildScope = createChildScopeClass(this);
25711 child = new this.$$ChildScope();
25713 child.$parent = parent;
25714 child.$$prevSibling = parent.$$childTail;
25715 if (parent.$$childHead) {
25716 parent.$$childTail.$$nextSibling = child;
25717 parent.$$childTail = child;
25719 parent.$$childHead = parent.$$childTail = child;
25722 // When the new scope is not isolated or we inherit from `this`, and
25723 // the parent scope is destroyed, the property `$$destroyed` is inherited
25724 // prototypically. In all other cases, this property needs to be set
25725 // when the parent scope is destroyed.
25726 // The listener needs to be added after the parent is set
25727 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
25734 * @name $rootScope.Scope#$watch
25738 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
25740 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
25741 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
25742 * its value when executed multiple times with the same input because it may be executed multiple
25743 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
25744 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
25745 * - The `listener` is called only when the value from the current `watchExpression` and the
25746 * previous call to `watchExpression` are not equal (with the exception of the initial run,
25747 * see below). Inequality is determined according to reference inequality,
25748 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
25749 * via the `!==` Javascript operator, unless `objectEquality == true`
25751 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
25752 * according to the {@link angular.equals} function. To save the value of the object for
25753 * later comparison, the {@link angular.copy} function is used. This therefore means that
25754 * watching complex objects will have adverse memory and performance implications.
25755 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
25756 * This is achieved by rerunning the watchers until no changes are detected. The rerun
25757 * iteration limit is 10 to prevent an infinite loop deadlock.
25760 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
25761 * you can register a `watchExpression` function with no `listener`. (Be prepared for
25762 * multiple calls to your `watchExpression` because it will execute multiple times in a
25763 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
25765 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
25766 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
25767 * watcher. In rare cases, this is undesirable because the listener is called when the result
25768 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
25769 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
25770 * listener was called due to initialization.
25776 // let's assume that scope was dependency injected as the $rootScope
25777 var scope = $rootScope;
25778 scope.name = 'misko';
25781 expect(scope.counter).toEqual(0);
25782 scope.$watch('name', function(newValue, oldValue) {
25783 scope.counter = scope.counter + 1;
25785 expect(scope.counter).toEqual(0);
25788 // the listener is always called during the first $digest loop after it was registered
25789 expect(scope.counter).toEqual(1);
25792 // but now it will not be called unless the value changes
25793 expect(scope.counter).toEqual(1);
25795 scope.name = 'adam';
25797 expect(scope.counter).toEqual(2);
25801 // Using a function as a watchExpression
25803 scope.foodCounter = 0;
25804 expect(scope.foodCounter).toEqual(0);
25806 // This function returns the value being watched. It is called for each turn of the $digest loop
25807 function() { return food; },
25808 // This is the change listener, called when the value returned from the above function changes
25809 function(newValue, oldValue) {
25810 if ( newValue !== oldValue ) {
25811 // Only increment the counter if the value changed
25812 scope.foodCounter = scope.foodCounter + 1;
25816 // No digest has been run so the counter will be zero
25817 expect(scope.foodCounter).toEqual(0);
25819 // Run the digest but since food has not changed count will still be zero
25821 expect(scope.foodCounter).toEqual(0);
25823 // Update food and run digest. Now the counter will increment
25824 food = 'cheeseburger';
25826 expect(scope.foodCounter).toEqual(1);
25832 * @param {(function()|string)} watchExpression Expression that is evaluated on each
25833 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
25834 * a call to the `listener`.
25836 * - `string`: Evaluated as {@link guide/expression expression}
25837 * - `function(scope)`: called with current `scope` as a parameter.
25838 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
25839 * of `watchExpression` changes.
25841 * - `newVal` contains the current value of the `watchExpression`
25842 * - `oldVal` contains the previous value of the `watchExpression`
25843 * - `scope` refers to the current scope
25844 * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
25845 * comparing for reference equality.
25846 * @returns {function()} Returns a deregistration function for this listener.
25848 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
25849 var get = $parse(watchExp);
25851 if (get.$$watchDelegate) {
25852 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
25855 array = scope.$$watchers,
25858 last: initWatchVal,
25860 exp: prettyPrintExpression || watchExp,
25861 eq: !!objectEquality
25864 lastDirtyWatch = null;
25866 if (!isFunction(listener)) {
25871 array = scope.$$watchers = [];
25873 // we use unshift since we use a while loop in $digest for speed.
25874 // the while loop reads in reverse order.
25875 array.unshift(watcher);
25876 incrementWatchersCount(this, 1);
25878 return function deregisterWatch() {
25879 if (arrayRemove(array, watcher) >= 0) {
25880 incrementWatchersCount(scope, -1);
25882 lastDirtyWatch = null;
25888 * @name $rootScope.Scope#$watchGroup
25892 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
25893 * If any one expression in the collection changes the `listener` is executed.
25895 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
25896 * call to $digest() to see if any items changes.
25897 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
25899 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
25900 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
25902 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
25903 * expression in `watchExpressions` changes
25904 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
25905 * those of `watchExpression`
25906 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
25907 * those of `watchExpression`
25908 * The `scope` refers to the current scope.
25909 * @returns {function()} Returns a de-registration function for all listeners.
25911 $watchGroup: function(watchExpressions, listener) {
25912 var oldValues = new Array(watchExpressions.length);
25913 var newValues = new Array(watchExpressions.length);
25914 var deregisterFns = [];
25916 var changeReactionScheduled = false;
25917 var firstRun = true;
25919 if (!watchExpressions.length) {
25920 // No expressions means we call the listener ASAP
25921 var shouldCall = true;
25922 self.$evalAsync(function() {
25923 if (shouldCall) listener(newValues, newValues, self);
25925 return function deregisterWatchGroup() {
25926 shouldCall = false;
25930 if (watchExpressions.length === 1) {
25931 // Special case size of one
25932 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
25933 newValues[0] = value;
25934 oldValues[0] = oldValue;
25935 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
25939 forEach(watchExpressions, function(expr, i) {
25940 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
25941 newValues[i] = value;
25942 oldValues[i] = oldValue;
25943 if (!changeReactionScheduled) {
25944 changeReactionScheduled = true;
25945 self.$evalAsync(watchGroupAction);
25948 deregisterFns.push(unwatchFn);
25951 function watchGroupAction() {
25952 changeReactionScheduled = false;
25956 listener(newValues, newValues, self);
25958 listener(newValues, oldValues, self);
25962 return function deregisterWatchGroup() {
25963 while (deregisterFns.length) {
25964 deregisterFns.shift()();
25972 * @name $rootScope.Scope#$watchCollection
25976 * Shallow watches the properties of an object and fires whenever any of the properties change
25977 * (for arrays, this implies watching the array items; for object maps, this implies watching
25978 * the properties). If a change is detected, the `listener` callback is fired.
25980 * - The `obj` collection is observed via standard $watch operation and is examined on every
25981 * call to $digest() to see if any items have been added, removed, or moved.
25982 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
25983 * adding, removing, and moving items belonging to an object or array.
25988 $scope.names = ['igor', 'matias', 'misko', 'james'];
25989 $scope.dataCount = 4;
25991 $scope.$watchCollection('names', function(newNames, oldNames) {
25992 $scope.dataCount = newNames.length;
25995 expect($scope.dataCount).toEqual(4);
25998 //still at 4 ... no changes
25999 expect($scope.dataCount).toEqual(4);
26001 $scope.names.pop();
26004 //now there's been a change
26005 expect($scope.dataCount).toEqual(3);
26009 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
26010 * expression value should evaluate to an object or an array which is observed on each
26011 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
26012 * collection will trigger a call to the `listener`.
26014 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
26015 * when a change is detected.
26016 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
26017 * - The `oldCollection` object is a copy of the former collection data.
26018 * Due to performance considerations, the`oldCollection` value is computed only if the
26019 * `listener` function declares two or more arguments.
26020 * - The `scope` argument refers to the current scope.
26022 * @returns {function()} Returns a de-registration function for this listener. When the
26023 * de-registration function is executed, the internal watch operation is terminated.
26025 $watchCollection: function(obj, listener) {
26026 $watchCollectionInterceptor.$stateful = true;
26029 // the current value, updated on each dirty-check run
26031 // a shallow copy of the newValue from the last dirty-check run,
26032 // updated to match newValue during dirty-check run
26034 // a shallow copy of the newValue from when the last change happened
26036 // only track veryOldValue if the listener is asking for it
26037 var trackVeryOldValue = (listener.length > 1);
26038 var changeDetected = 0;
26039 var changeDetector = $parse(obj, $watchCollectionInterceptor);
26040 var internalArray = [];
26041 var internalObject = {};
26042 var initRun = true;
26045 function $watchCollectionInterceptor(_value) {
26047 var newLength, key, bothNaN, newItem, oldItem;
26049 // If the new value is undefined, then return undefined as the watch may be a one-time watch
26050 if (isUndefined(newValue)) return;
26052 if (!isObject(newValue)) { // if primitive
26053 if (oldValue !== newValue) {
26054 oldValue = newValue;
26057 } else if (isArrayLike(newValue)) {
26058 if (oldValue !== internalArray) {
26059 // we are transitioning from something which was not an array into array.
26060 oldValue = internalArray;
26061 oldLength = oldValue.length = 0;
26065 newLength = newValue.length;
26067 if (oldLength !== newLength) {
26068 // if lengths do not match we need to trigger change notification
26070 oldValue.length = oldLength = newLength;
26072 // copy the items to oldValue and look for changes.
26073 for (var i = 0; i < newLength; i++) {
26074 oldItem = oldValue[i];
26075 newItem = newValue[i];
26077 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
26078 if (!bothNaN && (oldItem !== newItem)) {
26080 oldValue[i] = newItem;
26084 if (oldValue !== internalObject) {
26085 // we are transitioning from something which was not an object into object.
26086 oldValue = internalObject = {};
26090 // copy the items to oldValue and look for changes.
26092 for (key in newValue) {
26093 if (hasOwnProperty.call(newValue, key)) {
26095 newItem = newValue[key];
26096 oldItem = oldValue[key];
26098 if (key in oldValue) {
26099 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
26100 if (!bothNaN && (oldItem !== newItem)) {
26102 oldValue[key] = newItem;
26106 oldValue[key] = newItem;
26111 if (oldLength > newLength) {
26112 // we used to have more keys, need to find them and destroy them.
26114 for (key in oldValue) {
26115 if (!hasOwnProperty.call(newValue, key)) {
26117 delete oldValue[key];
26122 return changeDetected;
26125 function $watchCollectionAction() {
26128 listener(newValue, newValue, self);
26130 listener(newValue, veryOldValue, self);
26133 // make a copy for the next time a collection is changed
26134 if (trackVeryOldValue) {
26135 if (!isObject(newValue)) {
26137 veryOldValue = newValue;
26138 } else if (isArrayLike(newValue)) {
26139 veryOldValue = new Array(newValue.length);
26140 for (var i = 0; i < newValue.length; i++) {
26141 veryOldValue[i] = newValue[i];
26143 } else { // if object
26145 for (var key in newValue) {
26146 if (hasOwnProperty.call(newValue, key)) {
26147 veryOldValue[key] = newValue[key];
26154 return this.$watch(changeDetector, $watchCollectionAction);
26159 * @name $rootScope.Scope#$digest
26163 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
26164 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
26165 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
26166 * until no more listeners are firing. This means that it is possible to get into an infinite
26167 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
26168 * iterations exceeds 10.
26170 * Usually, you don't call `$digest()` directly in
26171 * {@link ng.directive:ngController controllers} or in
26172 * {@link ng.$compileProvider#directive directives}.
26173 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
26174 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
26176 * If you want to be notified whenever `$digest()` is called,
26177 * you can register a `watchExpression` function with
26178 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
26180 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
26185 scope.name = 'misko';
26188 expect(scope.counter).toEqual(0);
26189 scope.$watch('name', function(newValue, oldValue) {
26190 scope.counter = scope.counter + 1;
26192 expect(scope.counter).toEqual(0);
26195 // the listener is always called during the first $digest loop after it was registered
26196 expect(scope.counter).toEqual(1);
26199 // but now it will not be called unless the value changes
26200 expect(scope.counter).toEqual(1);
26202 scope.name = 'adam';
26204 expect(scope.counter).toEqual(2);
26208 $digest: function() {
26209 var watch, value, last, fn, get,
26213 next, current, target = this,
26217 beginPhase('$digest');
26218 // Check for changes to browser url that happened in sync before the call to $digest
26219 $browser.$$checkUrlChange();
26221 if (this === $rootScope && applyAsyncId !== null) {
26222 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
26223 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
26224 $browser.defer.cancel(applyAsyncId);
26228 lastDirtyWatch = null;
26230 do { // "while dirty" loop
26234 while (asyncQueue.length) {
26236 asyncTask = asyncQueue.shift();
26237 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
26239 $exceptionHandler(e);
26241 lastDirtyWatch = null;
26244 traverseScopesLoop:
26245 do { // "traverse the scopes" loop
26246 if ((watchers = current.$$watchers)) {
26247 // process our watches
26248 length = watchers.length;
26251 watch = watchers[length];
26252 // Most common watches are on primitives, in which case we can short
26253 // circuit it with === operator, only when === fails do we use .equals
26256 if ((value = get(current)) !== (last = watch.last) &&
26258 ? equals(value, last)
26259 : (typeof value === 'number' && typeof last === 'number'
26260 && isNaN(value) && isNaN(last)))) {
26262 lastDirtyWatch = watch;
26263 watch.last = watch.eq ? copy(value, null) : value;
26265 fn(value, ((last === initWatchVal) ? value : last), current);
26268 if (!watchLog[logIdx]) watchLog[logIdx] = [];
26269 watchLog[logIdx].push({
26270 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
26275 } else if (watch === lastDirtyWatch) {
26276 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
26277 // have already been tested.
26279 break traverseScopesLoop;
26283 $exceptionHandler(e);
26288 // Insanity Warning: scope depth-first traversal
26289 // yes, this code is a bit crazy, but it works and we have tests to prove it!
26290 // this piece should be kept in sync with the traversal in $broadcast
26291 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
26292 (current !== target && current.$$nextSibling)))) {
26293 while (current !== target && !(next = current.$$nextSibling)) {
26294 current = current.$parent;
26297 } while ((current = next));
26299 // `break traverseScopesLoop;` takes us to here
26301 if ((dirty || asyncQueue.length) && !(ttl--)) {
26303 throw $rootScopeMinErr('infdig',
26304 '{0} $digest() iterations reached. Aborting!\n' +
26305 'Watchers fired in the last 5 iterations: {1}',
26309 } while (dirty || asyncQueue.length);
26313 while (postDigestQueue.length) {
26315 postDigestQueue.shift()();
26317 $exceptionHandler(e);
26325 * @name $rootScope.Scope#$destroy
26326 * @eventType broadcast on scope being destroyed
26329 * Broadcasted when a scope and its children are being destroyed.
26331 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
26332 * clean up DOM bindings before an element is removed from the DOM.
26337 * @name $rootScope.Scope#$destroy
26341 * Removes the current scope (and all of its children) from the parent scope. Removal implies
26342 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
26343 * propagate to the current scope and its children. Removal also implies that the current
26344 * scope is eligible for garbage collection.
26346 * The `$destroy()` is usually used by directives such as
26347 * {@link ng.directive:ngRepeat ngRepeat} for managing the
26348 * unrolling of the loop.
26350 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
26351 * Application code can register a `$destroy` event handler that will give it a chance to
26352 * perform any necessary cleanup.
26354 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
26355 * clean up DOM bindings before an element is removed from the DOM.
26357 $destroy: function() {
26358 // We can't destroy a scope that has been already destroyed.
26359 if (this.$$destroyed) return;
26360 var parent = this.$parent;
26362 this.$broadcast('$destroy');
26363 this.$$destroyed = true;
26365 if (this === $rootScope) {
26366 //Remove handlers attached to window when $rootScope is removed
26367 $browser.$$applicationDestroyed();
26370 incrementWatchersCount(this, -this.$$watchersCount);
26371 for (var eventName in this.$$listenerCount) {
26372 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
26375 // sever all the references to parent scopes (after this cleanup, the current scope should
26376 // not be retained by any of our references and should be eligible for garbage collection)
26377 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
26378 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
26379 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
26380 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
26382 // Disable listeners, watchers and apply/digest methods
26383 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
26384 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
26385 this.$$listeners = {};
26387 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
26388 this.$$nextSibling = null;
26389 cleanUpScope(this);
26394 * @name $rootScope.Scope#$eval
26398 * Executes the `expression` on the current scope and returns the result. Any exceptions in
26399 * the expression are propagated (uncaught). This is useful when evaluating Angular
26404 var scope = ng.$rootScope.Scope();
26408 expect(scope.$eval('a+b')).toEqual(3);
26409 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
26412 * @param {(string|function())=} expression An angular expression to be executed.
26414 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26415 * - `function(scope)`: execute the function with the current `scope` parameter.
26417 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
26418 * @returns {*} The result of evaluating the expression.
26420 $eval: function(expr, locals) {
26421 return $parse(expr)(this, locals);
26426 * @name $rootScope.Scope#$evalAsync
26430 * Executes the expression on the current scope at a later point in time.
26432 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
26435 * - it will execute after the function that scheduled the evaluation (preferably before DOM
26437 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
26438 * `expression` execution.
26440 * Any exceptions from the execution of the expression are forwarded to the
26441 * {@link ng.$exceptionHandler $exceptionHandler} service.
26443 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
26444 * will be scheduled. However, it is encouraged to always call code that changes the model
26445 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
26447 * @param {(string|function())=} expression An angular expression to be executed.
26449 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26450 * - `function(scope)`: execute the function with the current `scope` parameter.
26452 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
26454 $evalAsync: function(expr, locals) {
26455 // if we are outside of an $digest loop and this is the first time we are scheduling async
26456 // task also schedule async auto-flush
26457 if (!$rootScope.$$phase && !asyncQueue.length) {
26458 $browser.defer(function() {
26459 if (asyncQueue.length) {
26460 $rootScope.$digest();
26465 asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
26468 $$postDigest: function(fn) {
26469 postDigestQueue.push(fn);
26474 * @name $rootScope.Scope#$apply
26478 * `$apply()` is used to execute an expression in angular from outside of the angular
26479 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
26480 * Because we are calling into the angular framework we need to perform proper scope life
26481 * cycle of {@link ng.$exceptionHandler exception handling},
26482 * {@link ng.$rootScope.Scope#$digest executing watches}.
26486 * # Pseudo-Code of `$apply()`
26488 function $apply(expr) {
26490 return $eval(expr);
26492 $exceptionHandler(e);
26500 * Scope's `$apply()` method transitions through the following stages:
26502 * 1. The {@link guide/expression expression} is executed using the
26503 * {@link ng.$rootScope.Scope#$eval $eval()} method.
26504 * 2. Any exceptions from the execution of the expression are forwarded to the
26505 * {@link ng.$exceptionHandler $exceptionHandler} service.
26506 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
26507 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
26510 * @param {(string|function())=} exp An angular expression to be executed.
26512 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26513 * - `function(scope)`: execute the function with current `scope` parameter.
26515 * @returns {*} The result of evaluating the expression.
26517 $apply: function(expr) {
26519 beginPhase('$apply');
26521 return this.$eval(expr);
26526 $exceptionHandler(e);
26529 $rootScope.$digest();
26531 $exceptionHandler(e);
26539 * @name $rootScope.Scope#$applyAsync
26543 * Schedule the invocation of $apply to occur at a later time. The actual time difference
26544 * varies across browsers, but is typically around ~10 milliseconds.
26546 * This can be used to queue up multiple expressions which need to be evaluated in the same
26549 * @param {(string|function())=} exp An angular expression to be executed.
26551 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26552 * - `function(scope)`: execute the function with current `scope` parameter.
26554 $applyAsync: function(expr) {
26556 expr && applyAsyncQueue.push($applyAsyncExpression);
26557 expr = $parse(expr);
26558 scheduleApplyAsync();
26560 function $applyAsyncExpression() {
26567 * @name $rootScope.Scope#$on
26571 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
26572 * discussion of event life cycle.
26574 * The event listener function format is: `function(event, args...)`. The `event` object
26575 * passed into the listener has the following attributes:
26577 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
26579 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
26580 * event propagates through the scope hierarchy, this property is set to null.
26581 * - `name` - `{string}`: name of the event.
26582 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
26583 * further event propagation (available only for events that were `$emit`-ed).
26584 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
26586 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
26588 * @param {string} name Event name to listen on.
26589 * @param {function(event, ...args)} listener Function to call when the event is emitted.
26590 * @returns {function()} Returns a deregistration function for this listener.
26592 $on: function(name, listener) {
26593 var namedListeners = this.$$listeners[name];
26594 if (!namedListeners) {
26595 this.$$listeners[name] = namedListeners = [];
26597 namedListeners.push(listener);
26599 var current = this;
26601 if (!current.$$listenerCount[name]) {
26602 current.$$listenerCount[name] = 0;
26604 current.$$listenerCount[name]++;
26605 } while ((current = current.$parent));
26608 return function() {
26609 var indexOfListener = namedListeners.indexOf(listener);
26610 if (indexOfListener !== -1) {
26611 namedListeners[indexOfListener] = null;
26612 decrementListenerCount(self, 1, name);
26620 * @name $rootScope.Scope#$emit
26624 * Dispatches an event `name` upwards through the scope hierarchy notifying the
26625 * registered {@link ng.$rootScope.Scope#$on} listeners.
26627 * The event life cycle starts at the scope on which `$emit` was called. All
26628 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
26629 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
26630 * registered listeners along the way. The event will stop propagating if one of the listeners
26633 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
26634 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
26636 * @param {string} name Event name to emit.
26637 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
26638 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
26640 $emit: function(name, args) {
26644 stopPropagation = false,
26647 targetScope: scope,
26648 stopPropagation: function() {stopPropagation = true;},
26649 preventDefault: function() {
26650 event.defaultPrevented = true;
26652 defaultPrevented: false
26654 listenerArgs = concat([event], arguments, 1),
26658 namedListeners = scope.$$listeners[name] || empty;
26659 event.currentScope = scope;
26660 for (i = 0, length = namedListeners.length; i < length; i++) {
26662 // if listeners were deregistered, defragment the array
26663 if (!namedListeners[i]) {
26664 namedListeners.splice(i, 1);
26670 //allow all listeners attached to the current scope to run
26671 namedListeners[i].apply(null, listenerArgs);
26673 $exceptionHandler(e);
26676 //if any listener on the current scope stops propagation, prevent bubbling
26677 if (stopPropagation) {
26678 event.currentScope = null;
26682 scope = scope.$parent;
26685 event.currentScope = null;
26693 * @name $rootScope.Scope#$broadcast
26697 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
26698 * registered {@link ng.$rootScope.Scope#$on} listeners.
26700 * The event life cycle starts at the scope on which `$broadcast` was called. All
26701 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
26702 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
26703 * scope and calls all registered listeners along the way. The event cannot be canceled.
26705 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
26706 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
26708 * @param {string} name Event name to broadcast.
26709 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
26710 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
26712 $broadcast: function(name, args) {
26718 targetScope: target,
26719 preventDefault: function() {
26720 event.defaultPrevented = true;
26722 defaultPrevented: false
26725 if (!target.$$listenerCount[name]) return event;
26727 var listenerArgs = concat([event], arguments, 1),
26728 listeners, i, length;
26730 //down while you can, then up and next sibling or up and next sibling until back at root
26731 while ((current = next)) {
26732 event.currentScope = current;
26733 listeners = current.$$listeners[name] || [];
26734 for (i = 0, length = listeners.length; i < length; i++) {
26735 // if listeners were deregistered, defragment the array
26736 if (!listeners[i]) {
26737 listeners.splice(i, 1);
26744 listeners[i].apply(null, listenerArgs);
26746 $exceptionHandler(e);
26750 // Insanity Warning: scope depth-first traversal
26751 // yes, this code is a bit crazy, but it works and we have tests to prove it!
26752 // this piece should be kept in sync with the traversal in $digest
26753 // (though it differs due to having the extra check for $$listenerCount)
26754 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
26755 (current !== target && current.$$nextSibling)))) {
26756 while (current !== target && !(next = current.$$nextSibling)) {
26757 current = current.$parent;
26762 event.currentScope = null;
26767 var $rootScope = new Scope();
26769 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
26770 var asyncQueue = $rootScope.$$asyncQueue = [];
26771 var postDigestQueue = $rootScope.$$postDigestQueue = [];
26772 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
26777 function beginPhase(phase) {
26778 if ($rootScope.$$phase) {
26779 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
26782 $rootScope.$$phase = phase;
26785 function clearPhase() {
26786 $rootScope.$$phase = null;
26789 function incrementWatchersCount(current, count) {
26791 current.$$watchersCount += count;
26792 } while ((current = current.$parent));
26795 function decrementListenerCount(current, count, name) {
26797 current.$$listenerCount[name] -= count;
26799 if (current.$$listenerCount[name] === 0) {
26800 delete current.$$listenerCount[name];
26802 } while ((current = current.$parent));
26806 * function used as an initial value for watchers.
26807 * because it's unique we can easily tell it apart from other values
26809 function initWatchVal() {}
26811 function flushApplyAsync() {
26812 while (applyAsyncQueue.length) {
26814 applyAsyncQueue.shift()();
26816 $exceptionHandler(e);
26819 applyAsyncId = null;
26822 function scheduleApplyAsync() {
26823 if (applyAsyncId === null) {
26824 applyAsyncId = $browser.defer(function() {
26825 $rootScope.$apply(flushApplyAsync);
26834 * @name $rootElement
26837 * The root element of Angular application. This is either the element where {@link
26838 * ng.directive:ngApp ngApp} was declared or the element passed into
26839 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
26840 * location where the application's {@link auto.$injector $injector} service gets
26841 * published, and can be retrieved using `$rootElement.injector()`.
26845 // the implementation is in angular.bootstrap
26849 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
26851 function $$SanitizeUriProvider() {
26852 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
26853 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
26857 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
26858 * urls during a[href] sanitization.
26860 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
26862 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
26863 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
26864 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
26865 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
26867 * @param {RegExp=} regexp New regexp to whitelist urls with.
26868 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
26869 * chaining otherwise.
26871 this.aHrefSanitizationWhitelist = function(regexp) {
26872 if (isDefined(regexp)) {
26873 aHrefSanitizationWhitelist = regexp;
26876 return aHrefSanitizationWhitelist;
26882 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
26883 * urls during img[src] sanitization.
26885 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
26887 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
26888 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
26889 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
26890 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
26892 * @param {RegExp=} regexp New regexp to whitelist urls with.
26893 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
26894 * chaining otherwise.
26896 this.imgSrcSanitizationWhitelist = function(regexp) {
26897 if (isDefined(regexp)) {
26898 imgSrcSanitizationWhitelist = regexp;
26901 return imgSrcSanitizationWhitelist;
26904 this.$get = function() {
26905 return function sanitizeUri(uri, isImage) {
26906 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
26908 normalizedVal = urlResolve(uri).href;
26909 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
26910 return 'unsafe:' + normalizedVal;
26917 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26918 * Any commits to this file should be reviewed with security in mind. *
26919 * Changes to this file can potentially create security vulnerabilities. *
26920 * An approval from 2 Core members with history of modifying *
26921 * this file is required. *
26923 * Does the change somehow allow for arbitrary javascript to be executed? *
26924 * Or allows for someone to change the prototype of built-in objects? *
26925 * Or gives undesired access to variables likes document or window? *
26926 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26928 var $sceMinErr = minErr('$sce');
26930 var SCE_CONTEXTS = {
26934 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
26935 // url. (e.g. ng-include, script src, templateUrl)
26936 RESOURCE_URL: 'resourceUrl',
26940 // Helper functions follow.
26942 function adjustMatcher(matcher) {
26943 if (matcher === 'self') {
26945 } else if (isString(matcher)) {
26946 // Strings match exactly except for 2 wildcards - '*' and '**'.
26947 // '*' matches any character except those from the set ':/.?&'.
26948 // '**' matches any character (like .* in a RegExp).
26949 // More than 2 *'s raises an error as it's ill defined.
26950 if (matcher.indexOf('***') > -1) {
26951 throw $sceMinErr('iwcard',
26952 'Illegal sequence *** in string matcher. String: {0}', matcher);
26954 matcher = escapeForRegexp(matcher).
26955 replace('\\*\\*', '.*').
26956 replace('\\*', '[^:/.?&;]*');
26957 return new RegExp('^' + matcher + '$');
26958 } else if (isRegExp(matcher)) {
26959 // The only other type of matcher allowed is a Regexp.
26960 // Match entire URL / disallow partial matches.
26961 // Flags are reset (i.e. no global, ignoreCase or multiline)
26962 return new RegExp('^' + matcher.source + '$');
26964 throw $sceMinErr('imatcher',
26965 'Matchers may only be "self", string patterns or RegExp objects');
26970 function adjustMatchers(matchers) {
26971 var adjustedMatchers = [];
26972 if (isDefined(matchers)) {
26973 forEach(matchers, function(matcher) {
26974 adjustedMatchers.push(adjustMatcher(matcher));
26977 return adjustedMatchers;
26983 * @name $sceDelegate
26988 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
26989 * Contextual Escaping (SCE)} services to AngularJS.
26991 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
26992 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
26993 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
26994 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
26995 * work because `$sce` delegates to `$sceDelegate` for these operations.
26997 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
26999 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
27000 * can override it completely to change the behavior of `$sce`, the common case would
27001 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
27002 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
27003 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
27004 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
27005 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
27010 * @name $sceDelegateProvider
27013 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
27014 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
27015 * that the URLs used for sourcing Angular templates are safe. Refer {@link
27016 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
27017 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
27019 * For the general details about this service in Angular, read the main page for {@link ng.$sce
27020 * Strict Contextual Escaping (SCE)}.
27022 * **Example**: Consider the following case. <a name="example"></a>
27024 * - your app is hosted at url `http://myapp.example.com/`
27025 * - but some of your templates are hosted on other domains you control such as
27026 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
27027 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
27029 * Here is what a secure configuration for this scenario might look like:
27032 * angular.module('myApp', []).config(function($sceDelegateProvider) {
27033 * $sceDelegateProvider.resourceUrlWhitelist([
27034 * // Allow same origin resource loads.
27036 * // Allow loading from our assets domain. Notice the difference between * and **.
27037 * 'http://srv*.assets.example.com/**'
27040 * // The blacklist overrides the whitelist so the open redirect here is blocked.
27041 * $sceDelegateProvider.resourceUrlBlacklist([
27042 * 'http://myapp.example.com/clickThru**'
27048 function $SceDelegateProvider() {
27049 this.SCE_CONTEXTS = SCE_CONTEXTS;
27051 // Resource URLs can also be trusted by policy.
27052 var resourceUrlWhitelist = ['self'],
27053 resourceUrlBlacklist = [];
27057 * @name $sceDelegateProvider#resourceUrlWhitelist
27060 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
27061 * provided. This must be an array or null. A snapshot of this array is used so further
27062 * changes to the array are ignored.
27064 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
27065 * allowed in this array.
27067 * <div class="alert alert-warning">
27068 * **Note:** an empty whitelist array will block all URLs!
27071 * @return {Array} the currently set whitelist array.
27073 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
27074 * same origin resource requests.
27077 * Sets/Gets the whitelist of trusted resource URLs.
27079 this.resourceUrlWhitelist = function(value) {
27080 if (arguments.length) {
27081 resourceUrlWhitelist = adjustMatchers(value);
27083 return resourceUrlWhitelist;
27088 * @name $sceDelegateProvider#resourceUrlBlacklist
27091 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
27092 * provided. This must be an array or null. A snapshot of this array is used so further
27093 * changes to the array are ignored.
27095 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
27096 * allowed in this array.
27098 * The typical usage for the blacklist is to **block
27099 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
27100 * these would otherwise be trusted but actually return content from the redirected domain.
27102 * Finally, **the blacklist overrides the whitelist** and has the final say.
27104 * @return {Array} the currently set blacklist array.
27106 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
27107 * is no blacklist.)
27110 * Sets/Gets the blacklist of trusted resource URLs.
27113 this.resourceUrlBlacklist = function(value) {
27114 if (arguments.length) {
27115 resourceUrlBlacklist = adjustMatchers(value);
27117 return resourceUrlBlacklist;
27120 this.$get = ['$injector', function($injector) {
27122 var htmlSanitizer = function htmlSanitizer(html) {
27123 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
27126 if ($injector.has('$sanitize')) {
27127 htmlSanitizer = $injector.get('$sanitize');
27131 function matchUrl(matcher, parsedUrl) {
27132 if (matcher === 'self') {
27133 return urlIsSameOrigin(parsedUrl);
27135 // definitely a regex. See adjustMatchers()
27136 return !!matcher.exec(parsedUrl.href);
27140 function isResourceUrlAllowedByPolicy(url) {
27141 var parsedUrl = urlResolve(url.toString());
27142 var i, n, allowed = false;
27143 // Ensure that at least one item from the whitelist allows this url.
27144 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
27145 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
27151 // Ensure that no item from the blacklist blocked this url.
27152 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
27153 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
27162 function generateHolderType(Base) {
27163 var holderType = function TrustedValueHolderType(trustedValue) {
27164 this.$$unwrapTrustedValue = function() {
27165 return trustedValue;
27169 holderType.prototype = new Base();
27171 holderType.prototype.valueOf = function sceValueOf() {
27172 return this.$$unwrapTrustedValue();
27174 holderType.prototype.toString = function sceToString() {
27175 return this.$$unwrapTrustedValue().toString();
27180 var trustedValueHolderBase = generateHolderType(),
27183 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
27184 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
27185 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
27186 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
27187 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
27191 * @name $sceDelegate#trustAs
27194 * Returns an object that is trusted by angular for use in specified strict
27195 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
27196 * attribute interpolation, any dom event binding attribute interpolation
27197 * such as for onclick, etc.) that uses the provided value.
27198 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
27200 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
27201 * resourceUrl, html, js and css.
27202 * @param {*} value The value that that should be considered trusted/safe.
27203 * @returns {*} A value that can be used to stand in for the provided `value` in places
27204 * where Angular expects a $sce.trustAs() return value.
27206 function trustAs(type, trustedValue) {
27207 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
27208 if (!Constructor) {
27209 throw $sceMinErr('icontext',
27210 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
27211 type, trustedValue);
27213 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
27214 return trustedValue;
27216 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
27217 // mutable objects, we ensure here that the value passed in is actually a string.
27218 if (typeof trustedValue !== 'string') {
27219 throw $sceMinErr('itype',
27220 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
27223 return new Constructor(trustedValue);
27228 * @name $sceDelegate#valueOf
27231 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
27232 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
27233 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
27235 * If the passed parameter is not a value that had been returned by {@link
27236 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
27238 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
27239 * call or anything else.
27240 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
27241 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
27242 * `value` unchanged.
27244 function valueOf(maybeTrusted) {
27245 if (maybeTrusted instanceof trustedValueHolderBase) {
27246 return maybeTrusted.$$unwrapTrustedValue();
27248 return maybeTrusted;
27254 * @name $sceDelegate#getTrusted
27257 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
27258 * returns the originally supplied value if the queried context type is a supertype of the
27259 * created type. If this condition isn't satisfied, throws an exception.
27261 * <div class="alert alert-danger">
27262 * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
27263 * (XSS) vulnerability in your application.
27266 * @param {string} type The kind of context in which this value is to be used.
27267 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
27268 * `$sceDelegate.trustAs`} call.
27269 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
27270 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
27272 function getTrusted(type, maybeTrusted) {
27273 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
27274 return maybeTrusted;
27276 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
27277 if (constructor && maybeTrusted instanceof constructor) {
27278 return maybeTrusted.$$unwrapTrustedValue();
27280 // If we get here, then we may only take one of two actions.
27281 // 1. sanitize the value for the requested type, or
27282 // 2. throw an exception.
27283 if (type === SCE_CONTEXTS.RESOURCE_URL) {
27284 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
27285 return maybeTrusted;
27287 throw $sceMinErr('insecurl',
27288 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
27289 maybeTrusted.toString());
27291 } else if (type === SCE_CONTEXTS.HTML) {
27292 return htmlSanitizer(maybeTrusted);
27294 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
27297 return { trustAs: trustAs,
27298 getTrusted: getTrusted,
27299 valueOf: valueOf };
27306 * @name $sceProvider
27309 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
27310 * - enable/disable Strict Contextual Escaping (SCE) in a module
27311 * - override the default implementation with a custom delegate
27313 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
27316 /* jshint maxlen: false*/
27325 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
27327 * # Strict Contextual Escaping
27329 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
27330 * contexts to result in a value that is marked as safe to use for that context. One example of
27331 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
27332 * to these contexts as privileged or SCE contexts.
27334 * As of version 1.2, Angular ships with SCE enabled by default.
27336 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
27337 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
27338 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
27339 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
27340 * to the top of your HTML document.
27342 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
27343 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
27345 * Here's an example of a binding in a privileged context:
27348 * <input ng-model="userHtml" aria-label="User input">
27349 * <div ng-bind-html="userHtml"></div>
27352 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
27353 * disabled, this application allows the user to render arbitrary HTML into the DIV.
27354 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
27355 * bindings. (HTML is just one example of a context where rendering user controlled input creates
27356 * security vulnerabilities.)
27358 * For the case of HTML, you might use a library, either on the client side, or on the server side,
27359 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
27361 * How would you ensure that every place that used these types of bindings was bound to a value that
27362 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
27363 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
27364 * properties/fields and forgot to update the binding to the sanitized value?
27366 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
27367 * determine that something explicitly says it's safe to use a value for binding in that
27368 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
27369 * for those values that you can easily tell are safe - because they were received from your server,
27370 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
27371 * allowing only the files in a specific directory to do this. Ensuring that the internal API
27372 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
27374 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
27375 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
27376 * obtain values that will be accepted by SCE / privileged contexts.
27379 * ## How does it work?
27381 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
27382 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
27383 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
27384 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
27386 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
27387 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
27391 * var ngBindHtmlDirective = ['$sce', function($sce) {
27392 * return function(scope, element, attr) {
27393 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
27394 * element.html(value || '');
27400 * ## Impact on loading templates
27402 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
27403 * `templateUrl`'s specified by {@link guide/directive directives}.
27405 * By default, Angular only loads templates from the same domain and protocol as the application
27406 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
27407 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
27408 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
27409 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
27413 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
27414 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
27415 * policy apply in addition to this and may further restrict whether the template is successfully
27416 * loaded. This means that without the right CORS policy, loading templates from a different domain
27417 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
27420 * ## This feels like too much overhead
27422 * It's important to remember that SCE only applies to interpolation expressions.
27424 * If your expressions are constant literals, they're automatically trusted and you don't need to
27425 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
27426 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
27428 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
27429 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
27431 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
27432 * templates in `ng-include` from your application's domain without having to even know about SCE.
27433 * It blocks loading templates from other domains or loading templates over http from an https
27434 * served document. You can change these by setting your own custom {@link
27435 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
27436 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
27438 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
27439 * application that's secure and can be audited to verify that with much more ease than bolting
27440 * security onto an application later.
27442 * <a name="contexts"></a>
27443 * ## What trusted context types are supported?
27445 * | Context | Notes |
27446 * |---------------------|----------------|
27447 * | `$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. |
27448 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
27449 * | `$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. |
27450 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
27451 * | `$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. |
27453 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
27455 * Each element in these arrays must be one of the following:
27458 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
27459 * domain** as the application document using the **same protocol**.
27460 * - **String** (except the special value `'self'`)
27461 * - The string is matched against the full *normalized / absolute URL* of the resource
27462 * being tested (substring matches are not good enough.)
27463 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
27464 * match themselves.
27465 * - `*`: matches zero or more occurrences of any character other than one of the following 6
27466 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
27468 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
27469 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
27470 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
27471 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
27472 * http://foo.example.com/templates/**).
27473 * - **RegExp** (*see caveat below*)
27474 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
27475 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
27476 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
27477 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
27478 * small number of cases. A `.` character in the regex used when matching the scheme or a
27479 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
27480 * is highly recommended to use the string patterns and only fall back to regular expressions
27481 * as a last resort.
27482 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
27483 * matched against the **entire** *normalized / absolute URL* of the resource being tested
27484 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
27485 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
27486 * - If you are generating your JavaScript from some other templating engine (not
27487 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
27488 * remember to escape your regular expression (and be aware that you might need more than
27489 * one level of escaping depending on your templating engine and the way you interpolated
27490 * the value.) Do make use of your platform's escaping mechanism as it might be good
27491 * enough before coding your own. E.g. Ruby has
27492 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
27493 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
27494 * Javascript lacks a similar built in function for escaping. Take a look at Google
27495 * Closure library's [goog.string.regExpEscape(s)](
27496 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
27498 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
27500 * ## Show me an example using SCE.
27502 * <example module="mySceApp" deps="angular-sanitize.js">
27503 * <file name="index.html">
27504 * <div ng-controller="AppController as myCtrl">
27505 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
27506 * <b>User comments</b><br>
27507 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
27508 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
27510 * <div class="well">
27511 * <div ng-repeat="userComment in myCtrl.userComments">
27512 * <b>{{userComment.name}}</b>:
27513 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
27520 * <file name="script.js">
27521 * angular.module('mySceApp', ['ngSanitize'])
27522 * .controller('AppController', ['$http', '$templateCache', '$sce',
27523 * function($http, $templateCache, $sce) {
27525 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
27526 * self.userComments = userComments;
27528 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
27529 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
27530 * 'sanitization."">Hover over this text.</span>');
27534 * <file name="test_data.json">
27536 * { "name": "Alice",
27538 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
27541 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
27546 * <file name="protractor.js" type="protractor">
27547 * describe('SCE doc demo', function() {
27548 * it('should sanitize untrusted values', function() {
27549 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
27550 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
27553 * it('should NOT sanitize explicitly trusted values', function() {
27554 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
27555 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
27556 * 'sanitization."">Hover over this text.</span>');
27564 * ## Can I disable SCE completely?
27566 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
27567 * for little coding overhead. It will be much harder to take an SCE disabled application and
27568 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
27569 * for cases where you have a lot of existing code that was written before SCE was introduced and
27570 * you're migrating them a module at a time.
27572 * That said, here's how you can completely disable SCE:
27575 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
27576 * // Completely disable SCE. For demonstration purposes only!
27577 * // Do not use in new projects.
27578 * $sceProvider.enabled(false);
27583 /* jshint maxlen: 100 */
27585 function $SceProvider() {
27586 var enabled = true;
27590 * @name $sceProvider#enabled
27593 * @param {boolean=} value If provided, then enables/disables SCE.
27594 * @return {boolean} true if SCE is enabled, false otherwise.
27597 * Enables/disables SCE and returns the current value.
27599 this.enabled = function(value) {
27600 if (arguments.length) {
27607 /* Design notes on the default implementation for SCE.
27609 * The API contract for the SCE delegate
27610 * -------------------------------------
27611 * The SCE delegate object must provide the following 3 methods:
27613 * - trustAs(contextEnum, value)
27614 * This method is used to tell the SCE service that the provided value is OK to use in the
27615 * contexts specified by contextEnum. It must return an object that will be accepted by
27616 * getTrusted() for a compatible contextEnum and return this value.
27619 * For values that were not produced by trustAs(), return them as is. For values that were
27620 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
27621 * trustAs is wrapping the given values into some type, this operation unwraps it when given
27624 * - getTrusted(contextEnum, value)
27625 * This function should return the a value that is safe to use in the context specified by
27626 * contextEnum or throw and exception otherwise.
27628 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
27629 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
27630 * instance, an implementation could maintain a registry of all trusted objects by context. In
27631 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
27632 * return the same object passed in if it was found in the registry under a compatible context or
27633 * throw an exception otherwise. An implementation might only wrap values some of the time based
27634 * on some criteria. getTrusted() might return a value and not throw an exception for special
27635 * constants or objects even if not wrapped. All such implementations fulfill this contract.
27638 * A note on the inheritance model for SCE contexts
27639 * ------------------------------------------------
27640 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
27641 * is purely an implementation details.
27643 * The contract is simply this:
27645 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
27646 * will also succeed.
27648 * Inheritance happens to capture this in a natural way. In some future, we
27649 * may not use inheritance anymore. That is OK because no code outside of
27650 * sce.js and sceSpecs.js would need to be aware of this detail.
27653 this.$get = ['$parse', '$sceDelegate', function(
27654 $parse, $sceDelegate) {
27655 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
27656 // the "expression(javascript expression)" syntax which is insecure.
27657 if (enabled && msie < 8) {
27658 throw $sceMinErr('iequirks',
27659 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
27660 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
27661 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
27664 var sce = shallowCopy(SCE_CONTEXTS);
27668 * @name $sce#isEnabled
27671 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
27672 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
27675 * Returns a boolean indicating if SCE is enabled.
27677 sce.isEnabled = function() {
27680 sce.trustAs = $sceDelegate.trustAs;
27681 sce.getTrusted = $sceDelegate.getTrusted;
27682 sce.valueOf = $sceDelegate.valueOf;
27685 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
27686 sce.valueOf = identity;
27691 * @name $sce#parseAs
27694 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
27695 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
27696 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
27699 * @param {string} type The kind of SCE context in which this result will be used.
27700 * @param {string} expression String expression to compile.
27701 * @returns {function(context, locals)} a function which represents the compiled expression:
27703 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27704 * are evaluated against (typically a scope object).
27705 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27708 sce.parseAs = function sceParseAs(type, expr) {
27709 var parsed = $parse(expr);
27710 if (parsed.literal && parsed.constant) {
27713 return $parse(expr, function(value) {
27714 return sce.getTrusted(type, value);
27721 * @name $sce#trustAs
27724 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
27725 * returns an object that is trusted by angular for use in specified strict contextual
27726 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
27727 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
27728 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
27731 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
27732 * resourceUrl, html, js and css.
27733 * @param {*} value The value that that should be considered trusted/safe.
27734 * @returns {*} A value that can be used to stand in for the provided `value` in places
27735 * where Angular expects a $sce.trustAs() return value.
27740 * @name $sce#trustAsHtml
27743 * Shorthand method. `$sce.trustAsHtml(value)` →
27744 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
27746 * @param {*} value The value to trustAs.
27747 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
27748 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
27749 * only accept expressions that are either literal constants or are the
27750 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
27755 * @name $sce#trustAsUrl
27758 * Shorthand method. `$sce.trustAsUrl(value)` →
27759 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
27761 * @param {*} value The value to trustAs.
27762 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
27763 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
27764 * only accept expressions that are either literal constants or are the
27765 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
27770 * @name $sce#trustAsResourceUrl
27773 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
27774 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
27776 * @param {*} value The value to trustAs.
27777 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
27778 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
27779 * only accept expressions that are either literal constants or are the return
27780 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
27785 * @name $sce#trustAsJs
27788 * Shorthand method. `$sce.trustAsJs(value)` →
27789 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
27791 * @param {*} value The value to trustAs.
27792 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
27793 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
27794 * only accept expressions that are either literal constants or are the
27795 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
27800 * @name $sce#getTrusted
27803 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
27804 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
27805 * originally supplied value if the queried context type is a supertype of the created type.
27806 * If this condition isn't satisfied, throws an exception.
27808 * @param {string} type The kind of context in which this value is to be used.
27809 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
27811 * @returns {*} The value the was originally provided to
27812 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
27813 * Otherwise, throws an exception.
27818 * @name $sce#getTrustedHtml
27821 * Shorthand method. `$sce.getTrustedHtml(value)` →
27822 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
27824 * @param {*} value The value to pass to `$sce.getTrusted`.
27825 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
27830 * @name $sce#getTrustedCss
27833 * Shorthand method. `$sce.getTrustedCss(value)` →
27834 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
27836 * @param {*} value The value to pass to `$sce.getTrusted`.
27837 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
27842 * @name $sce#getTrustedUrl
27845 * Shorthand method. `$sce.getTrustedUrl(value)` →
27846 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
27848 * @param {*} value The value to pass to `$sce.getTrusted`.
27849 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
27854 * @name $sce#getTrustedResourceUrl
27857 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
27858 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
27860 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
27861 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
27866 * @name $sce#getTrustedJs
27869 * Shorthand method. `$sce.getTrustedJs(value)` →
27870 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
27872 * @param {*} value The value to pass to `$sce.getTrusted`.
27873 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
27878 * @name $sce#parseAsHtml
27881 * Shorthand method. `$sce.parseAsHtml(expression string)` →
27882 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
27884 * @param {string} expression String expression to compile.
27885 * @returns {function(context, locals)} a function which represents the compiled expression:
27887 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27888 * are evaluated against (typically a scope object).
27889 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27895 * @name $sce#parseAsCss
27898 * Shorthand method. `$sce.parseAsCss(value)` →
27899 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
27901 * @param {string} expression String expression to compile.
27902 * @returns {function(context, locals)} a function which represents the compiled expression:
27904 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27905 * are evaluated against (typically a scope object).
27906 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27912 * @name $sce#parseAsUrl
27915 * Shorthand method. `$sce.parseAsUrl(value)` →
27916 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
27918 * @param {string} expression String expression to compile.
27919 * @returns {function(context, locals)} a function which represents the compiled expression:
27921 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27922 * are evaluated against (typically a scope object).
27923 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27929 * @name $sce#parseAsResourceUrl
27932 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
27933 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
27935 * @param {string} expression String expression to compile.
27936 * @returns {function(context, locals)} a function which represents the compiled expression:
27938 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27939 * are evaluated against (typically a scope object).
27940 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27946 * @name $sce#parseAsJs
27949 * Shorthand method. `$sce.parseAsJs(value)` →
27950 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
27952 * @param {string} expression String expression to compile.
27953 * @returns {function(context, locals)} a function which represents the compiled expression:
27955 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27956 * are evaluated against (typically a scope object).
27957 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27961 // Shorthand delegations.
27962 var parse = sce.parseAs,
27963 getTrusted = sce.getTrusted,
27964 trustAs = sce.trustAs;
27966 forEach(SCE_CONTEXTS, function(enumValue, name) {
27967 var lName = lowercase(name);
27968 sce[camelCase("parse_as_" + lName)] = function(expr) {
27969 return parse(enumValue, expr);
27971 sce[camelCase("get_trusted_" + lName)] = function(value) {
27972 return getTrusted(enumValue, value);
27974 sce[camelCase("trust_as_" + lName)] = function(value) {
27975 return trustAs(enumValue, value);
27984 * !!! This is an undocumented "private" service !!!
27987 * @requires $window
27988 * @requires $document
27990 * @property {boolean} history Does the browser support html5 history api ?
27991 * @property {boolean} transitions Does the browser support CSS transition events ?
27992 * @property {boolean} animations Does the browser support CSS animation events ?
27995 * This is very simple implementation of testing browser's features.
27997 function $SnifferProvider() {
27998 this.$get = ['$window', '$document', function($window, $document) {
27999 var eventSupport = {},
28000 // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
28001 // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
28002 isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
28003 hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
28005 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
28006 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
28007 document = $document[0] || {},
28009 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
28010 bodyStyle = document.body && document.body.style,
28011 transitions = false,
28012 animations = false,
28016 for (var prop in bodyStyle) {
28017 if (match = vendorRegex.exec(prop)) {
28018 vendorPrefix = match[0];
28019 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
28024 if (!vendorPrefix) {
28025 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
28028 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
28029 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
28031 if (android && (!transitions || !animations)) {
28032 transitions = isString(bodyStyle.webkitTransition);
28033 animations = isString(bodyStyle.webkitAnimation);
28039 // Android has history.pushState, but it does not update location correctly
28040 // so let's not use the history API at all.
28041 // http://code.google.com/p/android/issues/detail?id=17471
28042 // https://github.com/angular/angular.js/issues/904
28044 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
28045 // so let's not use the history API also
28046 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
28048 history: !!(hasHistoryPushState && !(android < 4) && !boxee),
28050 hasEvent: function(event) {
28051 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
28052 // it. In particular the event is not fired when backspace or delete key are pressed or
28053 // when cut operation is performed.
28054 // IE10+ implements 'input' event but it erroneously fires under various situations,
28055 // e.g. when placeholder changes, or a form is focused.
28056 if (event === 'input' && msie <= 11) return false;
28058 if (isUndefined(eventSupport[event])) {
28059 var divElm = document.createElement('div');
28060 eventSupport[event] = 'on' + event in divElm;
28063 return eventSupport[event];
28066 vendorPrefix: vendorPrefix,
28067 transitions: transitions,
28068 animations: animations,
28074 var $templateRequestMinErr = minErr('$compile');
28078 * @name $templateRequestProvider
28080 * Used to configure the options passed to the {@link $http} service when making a template request.
28082 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
28083 * requesting a template.
28085 function $TemplateRequestProvider() {
28091 * @name $templateRequestProvider#httpOptions
28093 * The options to be passed to the {@link $http} service when making the request.
28094 * You can use this to override options such as the "Accept" header for template requests.
28096 * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
28097 * options if not overridden here.
28099 * @param {string=} value new value for the {@link $http} options.
28100 * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
28102 this.httpOptions = function(val) {
28107 return httpOptions;
28112 * @name $templateRequest
28115 * The `$templateRequest` service runs security checks then downloads the provided template using
28116 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
28117 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
28118 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
28119 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
28120 * when `tpl` is of type string and `$templateCache` has the matching entry.
28122 * If you want to pass custom options to the `$http` service, such as setting the Accept header you
28123 * can configure this via {@link $templateRequestProvider#httpOptions}.
28125 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
28126 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
28128 * @return {Promise} a promise for the HTTP response data of the given URL.
28130 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
28132 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
28134 function handleRequestFn(tpl, ignoreRequestError) {
28135 handleRequestFn.totalPendingRequests++;
28137 // We consider the template cache holds only trusted templates, so
28138 // there's no need to go through whitelisting again for keys that already
28139 // are included in there. This also makes Angular accept any script
28140 // directive, no matter its name. However, we still need to unwrap trusted
28142 if (!isString(tpl) || !$templateCache.get(tpl)) {
28143 tpl = $sce.getTrustedResourceUrl(tpl);
28146 var transformResponse = $http.defaults && $http.defaults.transformResponse;
28148 if (isArray(transformResponse)) {
28149 transformResponse = transformResponse.filter(function(transformer) {
28150 return transformer !== defaultHttpResponseTransform;
28152 } else if (transformResponse === defaultHttpResponseTransform) {
28153 transformResponse = null;
28156 return $http.get(tpl, extend({
28157 cache: $templateCache,
28158 transformResponse: transformResponse
28160 ['finally'](function() {
28161 handleRequestFn.totalPendingRequests--;
28163 .then(function(response) {
28164 $templateCache.put(tpl, response.data);
28165 return response.data;
28168 function handleError(resp) {
28169 if (!ignoreRequestError) {
28170 throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
28171 tpl, resp.status, resp.statusText);
28173 return $q.reject(resp);
28177 handleRequestFn.totalPendingRequests = 0;
28179 return handleRequestFn;
28183 function $$TestabilityProvider() {
28184 this.$get = ['$rootScope', '$browser', '$location',
28185 function($rootScope, $browser, $location) {
28188 * @name $testability
28191 * The private $$testability service provides a collection of methods for use when debugging
28192 * or by automated test and debugging tools.
28194 var testability = {};
28197 * @name $$testability#findBindings
28200 * Returns an array of elements that are bound (via ng-bind or {{}})
28201 * to expressions matching the input.
28203 * @param {Element} element The element root to search from.
28204 * @param {string} expression The binding expression to match.
28205 * @param {boolean} opt_exactMatch If true, only returns exact matches
28206 * for the expression. Filters and whitespace are ignored.
28208 testability.findBindings = function(element, expression, opt_exactMatch) {
28209 var bindings = element.getElementsByClassName('ng-binding');
28211 forEach(bindings, function(binding) {
28212 var dataBinding = angular.element(binding).data('$binding');
28214 forEach(dataBinding, function(bindingName) {
28215 if (opt_exactMatch) {
28216 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
28217 if (matcher.test(bindingName)) {
28218 matches.push(binding);
28221 if (bindingName.indexOf(expression) != -1) {
28222 matches.push(binding);
28232 * @name $$testability#findModels
28235 * Returns an array of elements that are two-way found via ng-model to
28236 * expressions matching the input.
28238 * @param {Element} element The element root to search from.
28239 * @param {string} expression The model expression to match.
28240 * @param {boolean} opt_exactMatch If true, only returns exact matches
28241 * for the expression.
28243 testability.findModels = function(element, expression, opt_exactMatch) {
28244 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
28245 for (var p = 0; p < prefixes.length; ++p) {
28246 var attributeEquals = opt_exactMatch ? '=' : '*=';
28247 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
28248 var elements = element.querySelectorAll(selector);
28249 if (elements.length) {
28256 * @name $$testability#getLocation
28259 * Shortcut for getting the location in a browser agnostic way. Returns
28260 * the path, search, and hash. (e.g. /path?a=b#hash)
28262 testability.getLocation = function() {
28263 return $location.url();
28267 * @name $$testability#setLocation
28270 * Shortcut for navigating to a location without doing a full page reload.
28272 * @param {string} url The location url (path, search and hash,
28273 * e.g. /path?a=b#hash) to go to.
28275 testability.setLocation = function(url) {
28276 if (url !== $location.url()) {
28277 $location.url(url);
28278 $rootScope.$digest();
28283 * @name $$testability#whenStable
28286 * Calls the callback when $timeout and $http requests are completed.
28288 * @param {function} callback
28290 testability.whenStable = function(callback) {
28291 $browser.notifyWhenNoOutstandingRequests(callback);
28294 return testability;
28298 function $TimeoutProvider() {
28299 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
28300 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
28302 var deferreds = {};
28310 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
28311 * block and delegates any exceptions to
28312 * {@link ng.$exceptionHandler $exceptionHandler} service.
28314 * The return value of calling `$timeout` is a promise, which will be resolved when
28315 * the delay has passed and the timeout function, if provided, is executed.
28317 * To cancel a timeout request, call `$timeout.cancel(promise)`.
28319 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
28320 * synchronously flush the queue of deferred functions.
28322 * If you only want a promise that will be resolved after some specified delay
28323 * then you can call `$timeout` without the `fn` function.
28325 * @param {function()=} fn A function, whose execution should be delayed.
28326 * @param {number=} [delay=0] Delay in milliseconds.
28327 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
28328 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
28329 * @param {...*=} Pass additional parameters to the executed function.
28330 * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
28331 * will be resolved with the return value of the `fn` function.
28334 function timeout(fn, delay, invokeApply) {
28335 if (!isFunction(fn)) {
28336 invokeApply = delay;
28341 var args = sliceArgs(arguments, 3),
28342 skipApply = (isDefined(invokeApply) && !invokeApply),
28343 deferred = (skipApply ? $$q : $q).defer(),
28344 promise = deferred.promise,
28347 timeoutId = $browser.defer(function() {
28349 deferred.resolve(fn.apply(null, args));
28351 deferred.reject(e);
28352 $exceptionHandler(e);
28355 delete deferreds[promise.$$timeoutId];
28358 if (!skipApply) $rootScope.$apply();
28361 promise.$$timeoutId = timeoutId;
28362 deferreds[timeoutId] = deferred;
28370 * @name $timeout#cancel
28373 * Cancels a task associated with the `promise`. As a result of this, the promise will be
28374 * resolved with a rejection.
28376 * @param {Promise=} promise Promise returned by the `$timeout` function.
28377 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
28380 timeout.cancel = function(promise) {
28381 if (promise && promise.$$timeoutId in deferreds) {
28382 deferreds[promise.$$timeoutId].reject('canceled');
28383 delete deferreds[promise.$$timeoutId];
28384 return $browser.defer.cancel(promise.$$timeoutId);
28393 // NOTE: The usage of window and document instead of $window and $document here is
28394 // deliberate. This service depends on the specific behavior of anchor nodes created by the
28395 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
28396 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
28397 // doesn't know about mocked locations and resolves URLs to the real document - which is
28398 // exactly the behavior needed here. There is little value is mocking these out for this
28400 var urlParsingNode = window.document.createElement("a");
28401 var originUrl = urlResolve(window.location.href);
28406 * Implementation Notes for non-IE browsers
28407 * ----------------------------------------
28408 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
28409 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
28410 * URL will be resolved into an absolute URL in the context of the application document.
28411 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
28412 * properties are all populated to reflect the normalized URL. This approach has wide
28413 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
28414 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
28416 * Implementation Notes for IE
28417 * ---------------------------
28418 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
28419 * browsers. However, the parsed components will not be set if the URL assigned did not specify
28420 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
28421 * work around that by performing the parsing in a 2nd step by taking a previously normalized
28422 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
28423 * properties such as protocol, hostname, port, etc.
28426 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
28427 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
28428 * http://url.spec.whatwg.org/#urlutils
28429 * https://github.com/angular/angular.js/pull/2902
28430 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
28433 * @param {string} url The URL to be parsed.
28434 * @description Normalizes and parses a URL.
28435 * @returns {object} Returns the normalized URL as a dictionary.
28437 * | member name | Description |
28438 * |---------------|----------------|
28439 * | href | A normalized version of the provided URL if it was not an absolute URL |
28440 * | protocol | The protocol including the trailing colon |
28441 * | host | The host and port (if the port is non-default) of the normalizedUrl |
28442 * | search | The search params, minus the question mark |
28443 * | hash | The hash string, minus the hash symbol
28444 * | hostname | The hostname
28445 * | port | The port, without ":"
28446 * | pathname | The pathname, beginning with "/"
28449 function urlResolve(url) {
28453 // Normalize before parse. Refer Implementation Notes on why this is
28454 // done in two steps on IE.
28455 urlParsingNode.setAttribute("href", href);
28456 href = urlParsingNode.href;
28459 urlParsingNode.setAttribute('href', href);
28461 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
28463 href: urlParsingNode.href,
28464 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
28465 host: urlParsingNode.host,
28466 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
28467 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
28468 hostname: urlParsingNode.hostname,
28469 port: urlParsingNode.port,
28470 pathname: (urlParsingNode.pathname.charAt(0) === '/')
28471 ? urlParsingNode.pathname
28472 : '/' + urlParsingNode.pathname
28477 * Parse a request URL and determine whether this is a same-origin request as the application document.
28479 * @param {string|object} requestUrl The url of the request as a string that will be resolved
28480 * or a parsed URL object.
28481 * @returns {boolean} Whether the request is for the same origin as the application document.
28483 function urlIsSameOrigin(requestUrl) {
28484 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
28485 return (parsed.protocol === originUrl.protocol &&
28486 parsed.host === originUrl.host);
28494 * A reference to the browser's `window` object. While `window`
28495 * is globally available in JavaScript, it causes testability problems, because
28496 * it is a global variable. In angular we always refer to it through the
28497 * `$window` service, so it may be overridden, removed or mocked for testing.
28499 * Expressions, like the one defined for the `ngClick` directive in the example
28500 * below, are evaluated with respect to the current scope. Therefore, there is
28501 * no risk of inadvertently coding in a dependency on a global value in such an
28505 <example module="windowExample">
28506 <file name="index.html">
28508 angular.module('windowExample', [])
28509 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
28510 $scope.greeting = 'Hello, World!';
28511 $scope.doGreeting = function(greeting) {
28512 $window.alert(greeting);
28516 <div ng-controller="ExampleController">
28517 <input type="text" ng-model="greeting" aria-label="greeting" />
28518 <button ng-click="doGreeting(greeting)">ALERT</button>
28521 <file name="protractor.js" type="protractor">
28522 it('should display the greeting in the input box', function() {
28523 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
28524 // If we click the button it will block the test runner
28525 // element(':button').click();
28530 function $WindowProvider() {
28531 this.$get = valueFn(window);
28535 * @name $$cookieReader
28536 * @requires $document
28539 * This is a private service for reading cookies used by $http and ngCookies
28541 * @return {Object} a key/value map of the current cookies
28543 function $$CookieReader($document) {
28544 var rawDocument = $document[0] || {};
28545 var lastCookies = {};
28546 var lastCookieString = '';
28548 function safeDecodeURIComponent(str) {
28550 return decodeURIComponent(str);
28556 return function() {
28557 var cookieArray, cookie, i, index, name;
28558 var currentCookieString = rawDocument.cookie || '';
28560 if (currentCookieString !== lastCookieString) {
28561 lastCookieString = currentCookieString;
28562 cookieArray = lastCookieString.split('; ');
28565 for (i = 0; i < cookieArray.length; i++) {
28566 cookie = cookieArray[i];
28567 index = cookie.indexOf('=');
28568 if (index > 0) { //ignore nameless cookies
28569 name = safeDecodeURIComponent(cookie.substring(0, index));
28570 // the first value that is seen for a cookie is the most
28571 // specific one. values for the same cookie name that
28572 // follow are for less specific paths.
28573 if (isUndefined(lastCookies[name])) {
28574 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
28579 return lastCookies;
28583 $$CookieReader.$inject = ['$document'];
28585 function $$CookieReaderProvider() {
28586 this.$get = $$CookieReader;
28589 /* global currencyFilter: true,
28591 filterFilter: true,
28593 limitToFilter: true,
28594 lowercaseFilter: true,
28595 numberFilter: true,
28596 orderByFilter: true,
28597 uppercaseFilter: true,
28602 * @name $filterProvider
28605 * Filters are just functions which transform input to an output. However filters need to be
28606 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
28607 * annotated with dependencies and is responsible for creating a filter function.
28609 * <div class="alert alert-warning">
28610 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
28611 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
28612 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
28613 * (`myapp_subsection_filterx`).
28617 * // Filter registration
28618 * function MyModule($provide, $filterProvider) {
28619 * // create a service to demonstrate injection (not always needed)
28620 * $provide.value('greet', function(name){
28621 * return 'Hello ' + name + '!';
28624 * // register a filter factory which uses the
28625 * // greet service to demonstrate DI.
28626 * $filterProvider.register('greet', function(greet){
28627 * // return the filter function which uses the greet service
28628 * // to generate salutation
28629 * return function(text) {
28630 * // filters need to be forgiving so check input validity
28631 * return text && greet(text) || text;
28637 * The filter function is registered with the `$injector` under the filter name suffix with
28641 * it('should be the same instance', inject(
28642 * function($filterProvider) {
28643 * $filterProvider.register('reverse', function(){
28647 * function($filter, reverseFilter) {
28648 * expect($filter('reverse')).toBe(reverseFilter);
28653 * For more information about how angular filters work, and how to create your own filters, see
28654 * {@link guide/filter Filters} in the Angular Developer Guide.
28662 * Filters are used for formatting data displayed to the user.
28664 * The general syntax in templates is as follows:
28666 * {{ expression [| filter_name[:parameter_value] ... ] }}
28668 * @param {String} name Name of the filter function to retrieve
28669 * @return {Function} the filter function
28671 <example name="$filter" module="filterExample">
28672 <file name="index.html">
28673 <div ng-controller="MainCtrl">
28674 <h3>{{ originalText }}</h3>
28675 <h3>{{ filteredText }}</h3>
28679 <file name="script.js">
28680 angular.module('filterExample', [])
28681 .controller('MainCtrl', function($scope, $filter) {
28682 $scope.originalText = 'hello';
28683 $scope.filteredText = $filter('uppercase')($scope.originalText);
28688 $FilterProvider.$inject = ['$provide'];
28689 function $FilterProvider($provide) {
28690 var suffix = 'Filter';
28694 * @name $filterProvider#register
28695 * @param {string|Object} name Name of the filter function, or an object map of filters where
28696 * the keys are the filter names and the values are the filter factories.
28698 * <div class="alert alert-warning">
28699 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
28700 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
28701 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
28702 * (`myapp_subsection_filterx`).
28704 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
28705 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
28706 * of the registered filter instances.
28708 function register(name, factory) {
28709 if (isObject(name)) {
28711 forEach(name, function(filter, key) {
28712 filters[key] = register(key, filter);
28716 return $provide.factory(name + suffix, factory);
28719 this.register = register;
28721 this.$get = ['$injector', function($injector) {
28722 return function(name) {
28723 return $injector.get(name + suffix);
28727 ////////////////////////////////////////
28730 currencyFilter: false,
28732 filterFilter: false,
28734 limitToFilter: false,
28735 lowercaseFilter: false,
28736 numberFilter: false,
28737 orderByFilter: false,
28738 uppercaseFilter: false,
28741 register('currency', currencyFilter);
28742 register('date', dateFilter);
28743 register('filter', filterFilter);
28744 register('json', jsonFilter);
28745 register('limitTo', limitToFilter);
28746 register('lowercase', lowercaseFilter);
28747 register('number', numberFilter);
28748 register('orderBy', orderByFilter);
28749 register('uppercase', uppercaseFilter);
28758 * Selects a subset of items from `array` and returns it as a new array.
28760 * @param {Array} array The source array.
28761 * @param {string|Object|function()} expression The predicate to be used for selecting items from
28766 * - `string`: The string is used for matching against the contents of the `array`. All strings or
28767 * objects with string properties in `array` that match this string will be returned. This also
28768 * applies to nested object properties.
28769 * The predicate can be negated by prefixing the string with `!`.
28771 * - `Object`: A pattern object can be used to filter specific properties on objects contained
28772 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
28773 * which have property `name` containing "M" and property `phone` containing "1". A special
28774 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
28775 * property of the object or its nested object properties. That's equivalent to the simple
28776 * substring match with a `string` as described above. The predicate can be negated by prefixing
28777 * the string with `!`.
28778 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
28779 * not containing "M".
28781 * Note that a named property will match properties on the same level only, while the special
28782 * `$` property will match properties on the same level or deeper. E.g. an array item like
28783 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
28784 * **will** be matched by `{$: 'John'}`.
28786 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
28787 * The function is called for each element of the array, with the element, its index, and
28788 * the entire array itself as arguments.
28790 * The final result is an array of those elements that the predicate returned true for.
28792 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
28793 * determining if the expected value (from the filter expression) and actual value (from
28794 * the object in the array) should be considered a match.
28798 * - `function(actual, expected)`:
28799 * The function will be given the object value and the predicate value to compare and
28800 * should return true if both values should be considered equal.
28802 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
28803 * This is essentially strict comparison of expected and actual.
28805 * - `false|undefined`: A short hand for a function which will look for a substring match in case
28808 * Primitive values are converted to strings. Objects are not compared against primitives,
28809 * unless they have a custom `toString` method (e.g. `Date` objects).
28813 <file name="index.html">
28814 <div ng-init="friends = [{name:'John', phone:'555-1276'},
28815 {name:'Mary', phone:'800-BIG-MARY'},
28816 {name:'Mike', phone:'555-4321'},
28817 {name:'Adam', phone:'555-5678'},
28818 {name:'Julie', phone:'555-8765'},
28819 {name:'Juliette', phone:'555-5678'}]"></div>
28821 <label>Search: <input ng-model="searchText"></label>
28822 <table id="searchTextResults">
28823 <tr><th>Name</th><th>Phone</th></tr>
28824 <tr ng-repeat="friend in friends | filter:searchText">
28825 <td>{{friend.name}}</td>
28826 <td>{{friend.phone}}</td>
28830 <label>Any: <input ng-model="search.$"></label> <br>
28831 <label>Name only <input ng-model="search.name"></label><br>
28832 <label>Phone only <input ng-model="search.phone"></label><br>
28833 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
28834 <table id="searchObjResults">
28835 <tr><th>Name</th><th>Phone</th></tr>
28836 <tr ng-repeat="friendObj in friends | filter:search:strict">
28837 <td>{{friendObj.name}}</td>
28838 <td>{{friendObj.phone}}</td>
28842 <file name="protractor.js" type="protractor">
28843 var expectFriendNames = function(expectedNames, key) {
28844 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
28845 arr.forEach(function(wd, i) {
28846 expect(wd.getText()).toMatch(expectedNames[i]);
28851 it('should search across all fields when filtering with a string', function() {
28852 var searchText = element(by.model('searchText'));
28853 searchText.clear();
28854 searchText.sendKeys('m');
28855 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
28857 searchText.clear();
28858 searchText.sendKeys('76');
28859 expectFriendNames(['John', 'Julie'], 'friend');
28862 it('should search in specific fields when filtering with a predicate object', function() {
28863 var searchAny = element(by.model('search.$'));
28865 searchAny.sendKeys('i');
28866 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
28868 it('should use a equal comparison when comparator is true', function() {
28869 var searchName = element(by.model('search.name'));
28870 var strict = element(by.model('strict'));
28871 searchName.clear();
28872 searchName.sendKeys('Julie');
28874 expectFriendNames(['Julie'], 'friendObj');
28879 function filterFilter() {
28880 return function(array, expression, comparator) {
28881 if (!isArrayLike(array)) {
28882 if (array == null) {
28885 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
28889 var expressionType = getTypeForFilter(expression);
28891 var matchAgainstAnyProp;
28893 switch (expressionType) {
28895 predicateFn = expression;
28901 matchAgainstAnyProp = true;
28905 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
28911 return Array.prototype.filter.call(array, predicateFn);
28915 // Helper functions for `filterFilter`
28916 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
28917 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
28920 if (comparator === true) {
28921 comparator = equals;
28922 } else if (!isFunction(comparator)) {
28923 comparator = function(actual, expected) {
28924 if (isUndefined(actual)) {
28925 // No substring matching against `undefined`
28928 if ((actual === null) || (expected === null)) {
28929 // No substring matching against `null`; only match against `null`
28930 return actual === expected;
28932 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
28933 // Should not compare primitives against objects, unless they have custom `toString` method
28937 actual = lowercase('' + actual);
28938 expected = lowercase('' + expected);
28939 return actual.indexOf(expected) !== -1;
28943 predicateFn = function(item) {
28944 if (shouldMatchPrimitives && !isObject(item)) {
28945 return deepCompare(item, expression.$, comparator, false);
28947 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
28950 return predicateFn;
28953 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
28954 var actualType = getTypeForFilter(actual);
28955 var expectedType = getTypeForFilter(expected);
28957 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
28958 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
28959 } else if (isArray(actual)) {
28960 // In case `actual` is an array, consider it a match
28961 // if ANY of it's items matches `expected`
28962 return actual.some(function(item) {
28963 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
28967 switch (actualType) {
28970 if (matchAgainstAnyProp) {
28971 for (key in actual) {
28972 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
28976 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
28977 } else if (expectedType === 'object') {
28978 for (key in expected) {
28979 var expectedVal = expected[key];
28980 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
28984 var matchAnyProperty = key === '$';
28985 var actualVal = matchAnyProperty ? actual : actual[key];
28986 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
28992 return comparator(actual, expected);
28998 return comparator(actual, expected);
29002 // Used for easily differentiating between `null` and actual `object`
29003 function getTypeForFilter(val) {
29004 return (val === null) ? 'null' : typeof val;
29007 var MAX_DIGITS = 22;
29008 var DECIMAL_SEP = '.';
29009 var ZERO_CHAR = '0';
29017 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
29018 * symbol for current locale is used.
29020 * @param {number} amount Input to filter.
29021 * @param {string=} symbol Currency symbol or identifier to be displayed.
29022 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
29023 * @returns {string} Formatted number.
29027 <example module="currencyExample">
29028 <file name="index.html">
29030 angular.module('currencyExample', [])
29031 .controller('ExampleController', ['$scope', function($scope) {
29032 $scope.amount = 1234.56;
29035 <div ng-controller="ExampleController">
29036 <input type="number" ng-model="amount" aria-label="amount"> <br>
29037 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
29038 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
29039 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
29042 <file name="protractor.js" type="protractor">
29043 it('should init with 1234.56', function() {
29044 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
29045 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
29046 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
29048 it('should update', function() {
29049 if (browser.params.browser == 'safari') {
29050 // Safari does not understand the minus key. See
29051 // https://github.com/angular/protractor/issues/481
29054 element(by.model('amount')).clear();
29055 element(by.model('amount')).sendKeys('-1234');
29056 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
29057 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
29058 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
29063 currencyFilter.$inject = ['$locale'];
29064 function currencyFilter($locale) {
29065 var formats = $locale.NUMBER_FORMATS;
29066 return function(amount, currencySymbol, fractionSize) {
29067 if (isUndefined(currencySymbol)) {
29068 currencySymbol = formats.CURRENCY_SYM;
29071 if (isUndefined(fractionSize)) {
29072 fractionSize = formats.PATTERNS[1].maxFrac;
29075 // if null or undefined pass it through
29076 return (amount == null)
29078 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
29079 replace(/\u00A4/g, currencySymbol);
29089 * Formats a number as text.
29091 * If the input is null or undefined, it will just be returned.
29092 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
29093 * If the input is not a number an empty string is returned.
29096 * @param {number|string} number Number to format.
29097 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
29098 * If this is not provided then the fraction size is computed from the current locale's number
29099 * formatting pattern. In the case of the default locale, it will be 3.
29100 * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
29101 * locale (e.g., in the en_US locale it will have "." as the decimal separator and
29102 * include "," group separators after each third digit).
29105 <example module="numberFilterExample">
29106 <file name="index.html">
29108 angular.module('numberFilterExample', [])
29109 .controller('ExampleController', ['$scope', function($scope) {
29110 $scope.val = 1234.56789;
29113 <div ng-controller="ExampleController">
29114 <label>Enter number: <input ng-model='val'></label><br>
29115 Default formatting: <span id='number-default'>{{val | number}}</span><br>
29116 No fractions: <span>{{val | number:0}}</span><br>
29117 Negative number: <span>{{-val | number:4}}</span>
29120 <file name="protractor.js" type="protractor">
29121 it('should format numbers', function() {
29122 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
29123 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
29124 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
29127 it('should update', function() {
29128 element(by.model('val')).clear();
29129 element(by.model('val')).sendKeys('3374.333');
29130 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
29131 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
29132 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
29137 numberFilter.$inject = ['$locale'];
29138 function numberFilter($locale) {
29139 var formats = $locale.NUMBER_FORMATS;
29140 return function(number, fractionSize) {
29142 // if null or undefined pass it through
29143 return (number == null)
29145 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
29151 * Parse a number (as a string) into three components that can be used
29152 * for formatting the number.
29154 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
29156 * @param {string} numStr The number to parse
29157 * @return {object} An object describing this number, containing the following keys:
29158 * - d : an array of digits containing leading zeros as necessary
29159 * - i : the number of the digits in `d` that are to the left of the decimal point
29160 * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
29163 function parse(numStr) {
29164 var exponent = 0, digits, numberOfIntegerDigits;
29168 if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
29169 numStr = numStr.replace(DECIMAL_SEP, '');
29172 // Exponential form?
29173 if ((i = numStr.search(/e/i)) > 0) {
29174 // Work out the exponent.
29175 if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
29176 numberOfIntegerDigits += +numStr.slice(i + 1);
29177 numStr = numStr.substring(0, i);
29178 } else if (numberOfIntegerDigits < 0) {
29179 // There was no decimal point or exponent so it is an integer.
29180 numberOfIntegerDigits = numStr.length;
29183 // Count the number of leading zeros.
29184 for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
29186 if (i == (zeros = numStr.length)) {
29187 // The digits are all zero.
29189 numberOfIntegerDigits = 1;
29191 // Count the number of trailing zeros
29193 while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
29195 // Trailing zeros are insignificant so ignore them
29196 numberOfIntegerDigits -= i;
29198 // Convert string to array of digits without leading/trailing zeros.
29199 for (j = 0; i <= zeros; i++, j++) {
29200 digits[j] = +numStr.charAt(i);
29204 // If the number overflows the maximum allowed digits then use an exponent.
29205 if (numberOfIntegerDigits > MAX_DIGITS) {
29206 digits = digits.splice(0, MAX_DIGITS - 1);
29207 exponent = numberOfIntegerDigits - 1;
29208 numberOfIntegerDigits = 1;
29211 return { d: digits, e: exponent, i: numberOfIntegerDigits };
29215 * Round the parsed number to the specified number of decimal places
29216 * This function changed the parsedNumber in-place
29218 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
29219 var digits = parsedNumber.d;
29220 var fractionLen = digits.length - parsedNumber.i;
29222 // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
29223 fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
29225 // The index of the digit to where rounding is to occur
29226 var roundAt = fractionSize + parsedNumber.i;
29227 var digit = digits[roundAt];
29230 // Drop fractional digits beyond `roundAt`
29231 digits.splice(Math.max(parsedNumber.i, roundAt));
29233 // Set non-fractional digits beyond `roundAt` to 0
29234 for (var j = roundAt; j < digits.length; j++) {
29238 // We rounded to zero so reset the parsedNumber
29239 fractionLen = Math.max(0, fractionLen);
29240 parsedNumber.i = 1;
29241 digits.length = Math.max(1, roundAt = fractionSize + 1);
29243 for (var i = 1; i < roundAt; i++) digits[i] = 0;
29247 if (roundAt - 1 < 0) {
29248 for (var k = 0; k > roundAt; k--) {
29255 digits[roundAt - 1]++;
29259 // Pad out with zeros to get the required fraction length
29260 for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
29263 // Do any carrying, e.g. a digit was rounded up to 10
29264 var carry = digits.reduceRight(function(carry, d, i, digits) {
29266 digits[i] = d % 10;
29267 return Math.floor(d / 10);
29270 digits.unshift(carry);
29276 * Format a number into a string
29277 * @param {number} number The number to format
29279 * minFrac, // the minimum number of digits required in the fraction part of the number
29280 * maxFrac, // the maximum number of digits required in the fraction part of the number
29281 * gSize, // number of digits in each group of separated digits
29282 * lgSize, // number of digits in the last group of digits before the decimal separator
29283 * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
29284 * posPre, // the string to go in front of a positive number
29285 * negSuf, // the string to go after a negative number (e.g. `)`)
29286 * posSuf // the string to go after a positive number
29288 * @param {string} groupSep The string to separate groups of number (e.g. `,`)
29289 * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
29290 * @param {[type]} fractionSize The size of the fractional part of the number
29291 * @return {string} The number formatted as a string
29293 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
29295 if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
29297 var isInfinity = !isFinite(number);
29298 var isZero = false;
29299 var numStr = Math.abs(number) + '',
29300 formattedText = '',
29304 formattedText = '\u221e';
29306 parsedNumber = parse(numStr);
29308 roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
29310 var digits = parsedNumber.d;
29311 var integerLen = parsedNumber.i;
29312 var exponent = parsedNumber.e;
29314 isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
29316 // pad zeros for small numbers
29317 while (integerLen < 0) {
29322 // extract decimals digits
29323 if (integerLen > 0) {
29324 decimals = digits.splice(integerLen);
29330 // format the integer digits with grouping separators
29332 if (digits.length >= pattern.lgSize) {
29333 groups.unshift(digits.splice(-pattern.lgSize).join(''));
29335 while (digits.length > pattern.gSize) {
29336 groups.unshift(digits.splice(-pattern.gSize).join(''));
29338 if (digits.length) {
29339 groups.unshift(digits.join(''));
29341 formattedText = groups.join(groupSep);
29343 // append the decimal digits
29344 if (decimals.length) {
29345 formattedText += decimalSep + decimals.join('');
29349 formattedText += 'e+' + exponent;
29352 if (number < 0 && !isZero) {
29353 return pattern.negPre + formattedText + pattern.negSuf;
29355 return pattern.posPre + formattedText + pattern.posSuf;
29359 function padNumber(num, digits, trim, negWrap) {
29361 if (num < 0 || (negWrap && num <= 0)) {
29370 while (num.length < digits) num = ZERO_CHAR + num;
29372 num = num.substr(num.length - digits);
29378 function dateGetter(name, size, offset, trim, negWrap) {
29379 offset = offset || 0;
29380 return function(date) {
29381 var value = date['get' + name]();
29382 if (offset > 0 || value > -offset) {
29385 if (value === 0 && offset == -12) value = 12;
29386 return padNumber(value, size, trim, negWrap);
29390 function dateStrGetter(name, shortForm, standAlone) {
29391 return function(date, formats) {
29392 var value = date['get' + name]();
29393 var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
29394 var get = uppercase(propPrefix + name);
29396 return formats[get][value];
29400 function timeZoneGetter(date, formats, offset) {
29401 var zone = -1 * offset;
29402 var paddedZone = (zone >= 0) ? "+" : "";
29404 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
29405 padNumber(Math.abs(zone % 60), 2);
29410 function getFirstThursdayOfYear(year) {
29411 // 0 = index of January
29412 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
29413 // 4 = index of Thursday (+1 to account for 1st = 5)
29414 // 11 = index of *next* Thursday (+1 account for 1st = 12)
29415 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
29418 function getThursdayThisWeek(datetime) {
29419 return new Date(datetime.getFullYear(), datetime.getMonth(),
29420 // 4 = index of Thursday
29421 datetime.getDate() + (4 - datetime.getDay()));
29424 function weekGetter(size) {
29425 return function(date) {
29426 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
29427 thisThurs = getThursdayThisWeek(date);
29429 var diff = +thisThurs - +firstThurs,
29430 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
29432 return padNumber(result, size);
29436 function ampmGetter(date, formats) {
29437 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
29440 function eraGetter(date, formats) {
29441 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
29444 function longEraGetter(date, formats) {
29445 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
29448 var DATE_FORMATS = {
29449 yyyy: dateGetter('FullYear', 4, 0, false, true),
29450 yy: dateGetter('FullYear', 2, 0, true, true),
29451 y: dateGetter('FullYear', 1, 0, false, true),
29452 MMMM: dateStrGetter('Month'),
29453 MMM: dateStrGetter('Month', true),
29454 MM: dateGetter('Month', 2, 1),
29455 M: dateGetter('Month', 1, 1),
29456 LLLL: dateStrGetter('Month', false, true),
29457 dd: dateGetter('Date', 2),
29458 d: dateGetter('Date', 1),
29459 HH: dateGetter('Hours', 2),
29460 H: dateGetter('Hours', 1),
29461 hh: dateGetter('Hours', 2, -12),
29462 h: dateGetter('Hours', 1, -12),
29463 mm: dateGetter('Minutes', 2),
29464 m: dateGetter('Minutes', 1),
29465 ss: dateGetter('Seconds', 2),
29466 s: dateGetter('Seconds', 1),
29467 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
29468 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
29469 sss: dateGetter('Milliseconds', 3),
29470 EEEE: dateStrGetter('Day'),
29471 EEE: dateStrGetter('Day', true),
29479 GGGG: longEraGetter
29482 var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
29483 NUMBER_STRING = /^\-?\d+$/;
29491 * Formats `date` to a string based on the requested `format`.
29493 * `format` string can be composed of the following elements:
29495 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
29496 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
29497 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
29498 * * `'MMMM'`: Month in year (January-December)
29499 * * `'MMM'`: Month in year (Jan-Dec)
29500 * * `'MM'`: Month in year, padded (01-12)
29501 * * `'M'`: Month in year (1-12)
29502 * * `'LLLL'`: Stand-alone month in year (January-December)
29503 * * `'dd'`: Day in month, padded (01-31)
29504 * * `'d'`: Day in month (1-31)
29505 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
29506 * * `'EEE'`: Day in Week, (Sun-Sat)
29507 * * `'HH'`: Hour in day, padded (00-23)
29508 * * `'H'`: Hour in day (0-23)
29509 * * `'hh'`: Hour in AM/PM, padded (01-12)
29510 * * `'h'`: Hour in AM/PM, (1-12)
29511 * * `'mm'`: Minute in hour, padded (00-59)
29512 * * `'m'`: Minute in hour (0-59)
29513 * * `'ss'`: Second in minute, padded (00-59)
29514 * * `'s'`: Second in minute (0-59)
29515 * * `'sss'`: Millisecond in second, padded (000-999)
29516 * * `'a'`: AM/PM marker
29517 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
29518 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
29519 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
29520 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
29521 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
29523 * `format` string can also be one of the following predefined
29524 * {@link guide/i18n localizable formats}:
29526 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
29527 * (e.g. Sep 3, 2010 12:05:08 PM)
29528 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
29529 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
29530 * (e.g. Friday, September 3, 2010)
29531 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
29532 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
29533 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
29534 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
29535 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
29537 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
29538 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
29539 * (e.g. `"h 'o''clock'"`).
29541 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
29542 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
29543 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
29544 * specified in the string input, the time is considered to be in the local timezone.
29545 * @param {string=} format Formatting rules (see Description). If not specified,
29546 * `mediumDate` is used.
29547 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
29548 * continental US time zone abbreviations, but for general use, use a time zone offset, for
29549 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
29550 * If not specified, the timezone of the browser will be used.
29551 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
29555 <file name="index.html">
29556 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
29557 <span>{{1288323623006 | date:'medium'}}</span><br>
29558 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
29559 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
29560 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
29561 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
29562 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
29563 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
29565 <file name="protractor.js" type="protractor">
29566 it('should format date', function() {
29567 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
29568 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
29569 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
29570 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
29571 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
29572 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
29573 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
29574 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
29579 dateFilter.$inject = ['$locale'];
29580 function dateFilter($locale) {
29583 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
29584 // 1 2 3 4 5 6 7 8 9 10 11
29585 function jsonStringToDate(string) {
29587 if (match = string.match(R_ISO8601_STR)) {
29588 var date = new Date(0),
29591 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
29592 timeSetter = match[8] ? date.setUTCHours : date.setHours;
29595 tzHour = toInt(match[9] + match[10]);
29596 tzMin = toInt(match[9] + match[11]);
29598 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
29599 var h = toInt(match[4] || 0) - tzHour;
29600 var m = toInt(match[5] || 0) - tzMin;
29601 var s = toInt(match[6] || 0);
29602 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
29603 timeSetter.call(date, h, m, s, ms);
29610 return function(date, format, timezone) {
29615 format = format || 'mediumDate';
29616 format = $locale.DATETIME_FORMATS[format] || format;
29617 if (isString(date)) {
29618 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
29621 if (isNumber(date)) {
29622 date = new Date(date);
29625 if (!isDate(date) || !isFinite(date.getTime())) {
29630 match = DATE_FORMATS_SPLIT.exec(format);
29632 parts = concat(parts, match, 1);
29633 format = parts.pop();
29635 parts.push(format);
29640 var dateTimezoneOffset = date.getTimezoneOffset();
29642 dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
29643 date = convertTimezoneToLocal(date, timezone, true);
29645 forEach(parts, function(value) {
29646 fn = DATE_FORMATS[value];
29647 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
29648 : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
29662 * Allows you to convert a JavaScript object into JSON string.
29664 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
29665 * the binding is automatically converted to JSON.
29667 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
29668 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
29669 * @returns {string} JSON string.
29674 <file name="index.html">
29675 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
29676 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
29678 <file name="protractor.js" type="protractor">
29679 it('should jsonify filtered objects', function() {
29680 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
29681 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
29687 function jsonFilter() {
29688 return function(object, spacing) {
29689 if (isUndefined(spacing)) {
29692 return toJson(object, spacing);
29702 * Converts string to lowercase.
29703 * @see angular.lowercase
29705 var lowercaseFilter = valueFn(lowercase);
29713 * Converts string to uppercase.
29714 * @see angular.uppercase
29716 var uppercaseFilter = valueFn(uppercase);
29724 * Creates a new array or string containing only a specified number of elements. The elements
29725 * are taken from either the beginning or the end of the source array, string or number, as specified by
29726 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
29727 * converted to a string.
29729 * @param {Array|string|number} input Source array, string or number to be limited.
29730 * @param {string|number} limit The length of the returned array or string. If the `limit` number
29731 * is positive, `limit` number of items from the beginning of the source array/string are copied.
29732 * If the number is negative, `limit` number of items from the end of the source array/string
29733 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
29734 * the input will be returned unchanged.
29735 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
29736 * indicates an offset from the end of `input`. Defaults to `0`.
29737 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
29738 * had less than `limit` elements.
29741 <example module="limitToExample">
29742 <file name="index.html">
29744 angular.module('limitToExample', [])
29745 .controller('ExampleController', ['$scope', function($scope) {
29746 $scope.numbers = [1,2,3,4,5,6,7,8,9];
29747 $scope.letters = "abcdefghi";
29748 $scope.longNumber = 2345432342;
29749 $scope.numLimit = 3;
29750 $scope.letterLimit = 3;
29751 $scope.longNumberLimit = 3;
29754 <div ng-controller="ExampleController">
29756 Limit {{numbers}} to:
29757 <input type="number" step="1" ng-model="numLimit">
29759 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
29761 Limit {{letters}} to:
29762 <input type="number" step="1" ng-model="letterLimit">
29764 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
29766 Limit {{longNumber}} to:
29767 <input type="number" step="1" ng-model="longNumberLimit">
29769 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
29772 <file name="protractor.js" type="protractor">
29773 var numLimitInput = element(by.model('numLimit'));
29774 var letterLimitInput = element(by.model('letterLimit'));
29775 var longNumberLimitInput = element(by.model('longNumberLimit'));
29776 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
29777 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
29778 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
29780 it('should limit the number array to first three items', function() {
29781 expect(numLimitInput.getAttribute('value')).toBe('3');
29782 expect(letterLimitInput.getAttribute('value')).toBe('3');
29783 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
29784 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
29785 expect(limitedLetters.getText()).toEqual('Output letters: abc');
29786 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
29789 // There is a bug in safari and protractor that doesn't like the minus key
29790 // it('should update the output when -3 is entered', function() {
29791 // numLimitInput.clear();
29792 // numLimitInput.sendKeys('-3');
29793 // letterLimitInput.clear();
29794 // letterLimitInput.sendKeys('-3');
29795 // longNumberLimitInput.clear();
29796 // longNumberLimitInput.sendKeys('-3');
29797 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
29798 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
29799 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
29802 it('should not exceed the maximum size of input array', function() {
29803 numLimitInput.clear();
29804 numLimitInput.sendKeys('100');
29805 letterLimitInput.clear();
29806 letterLimitInput.sendKeys('100');
29807 longNumberLimitInput.clear();
29808 longNumberLimitInput.sendKeys('100');
29809 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
29810 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
29811 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
29816 function limitToFilter() {
29817 return function(input, limit, begin) {
29818 if (Math.abs(Number(limit)) === Infinity) {
29819 limit = Number(limit);
29821 limit = toInt(limit);
29823 if (isNaN(limit)) return input;
29825 if (isNumber(input)) input = input.toString();
29826 if (!isArray(input) && !isString(input)) return input;
29828 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
29829 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
29832 return input.slice(begin, begin + limit);
29835 return input.slice(limit, input.length);
29837 return input.slice(Math.max(0, begin + limit), begin);
29849 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
29850 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
29851 * as expected, make sure they are actually being saved as numbers and not strings.
29852 * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
29854 * @param {Array} array The array (or array-like object) to sort.
29855 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
29856 * used by the comparator to determine the order of elements.
29860 * - `function`: Getter function. The result of this function will be sorted using the
29861 * `<`, `===`, `>` operator.
29862 * - `string`: An Angular expression. The result of this expression is used to compare elements
29863 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
29864 * 3 first characters of a property called `name`). The result of a constant expression
29865 * is interpreted as a property name to be used in comparisons (for example `"special name"`
29866 * to sort object by the value of their `special name` property). An expression can be
29867 * optionally prefixed with `+` or `-` to control ascending or descending sort order
29868 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
29869 * element itself is used to compare where sorting.
29870 * - `Array`: An array of function or string predicates. The first predicate in the array
29871 * is used for sorting, but when two items are equivalent, the next predicate is used.
29873 * If the predicate is missing or empty then it defaults to `'+'`.
29875 * @param {boolean=} reverse Reverse the order of the array.
29876 * @returns {Array} Sorted copy of the source array.
29880 * The example below demonstrates a simple ngRepeat, where the data is sorted
29881 * by age in descending order (predicate is set to `'-age'`).
29882 * `reverse` is not set, which means it defaults to `false`.
29883 <example module="orderByExample">
29884 <file name="index.html">
29885 <div ng-controller="ExampleController">
29886 <table class="friend">
29889 <th>Phone Number</th>
29892 <tr ng-repeat="friend in friends | orderBy:'-age'">
29893 <td>{{friend.name}}</td>
29894 <td>{{friend.phone}}</td>
29895 <td>{{friend.age}}</td>
29900 <file name="script.js">
29901 angular.module('orderByExample', [])
29902 .controller('ExampleController', ['$scope', function($scope) {
29904 [{name:'John', phone:'555-1212', age:10},
29905 {name:'Mary', phone:'555-9876', age:19},
29906 {name:'Mike', phone:'555-4321', age:21},
29907 {name:'Adam', phone:'555-5678', age:35},
29908 {name:'Julie', phone:'555-8765', age:29}];
29913 * The predicate and reverse parameters can be controlled dynamically through scope properties,
29914 * as shown in the next example.
29916 <example module="orderByExample">
29917 <file name="index.html">
29918 <div ng-controller="ExampleController">
29919 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
29921 <button ng-click="predicate=''">Set to unsorted</button>
29922 <table class="friend">
29925 <button ng-click="order('name')">Name</button>
29926 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
29929 <button ng-click="order('phone')">Phone Number</button>
29930 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
29933 <button ng-click="order('age')">Age</button>
29934 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
29937 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
29938 <td>{{friend.name}}</td>
29939 <td>{{friend.phone}}</td>
29940 <td>{{friend.age}}</td>
29945 <file name="script.js">
29946 angular.module('orderByExample', [])
29947 .controller('ExampleController', ['$scope', function($scope) {
29949 [{name:'John', phone:'555-1212', age:10},
29950 {name:'Mary', phone:'555-9876', age:19},
29951 {name:'Mike', phone:'555-4321', age:21},
29952 {name:'Adam', phone:'555-5678', age:35},
29953 {name:'Julie', phone:'555-8765', age:29}];
29954 $scope.predicate = 'age';
29955 $scope.reverse = true;
29956 $scope.order = function(predicate) {
29957 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
29958 $scope.predicate = predicate;
29962 <file name="style.css">
29966 .sortorder.reverse:after {
29972 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
29973 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
29974 * desired parameters.
29979 <example module="orderByExample">
29980 <file name="index.html">
29981 <div ng-controller="ExampleController">
29982 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
29983 <table class="friend">
29986 <button ng-click="order('name')">Name</button>
29987 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
29990 <button ng-click="order('phone')">Phone Number</button>
29991 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
29994 <button ng-click="order('age')">Age</button>
29995 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
29998 <tr ng-repeat="friend in friends">
29999 <td>{{friend.name}}</td>
30000 <td>{{friend.phone}}</td>
30001 <td>{{friend.age}}</td>
30007 <file name="script.js">
30008 angular.module('orderByExample', [])
30009 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
30010 var orderBy = $filter('orderBy');
30012 { name: 'John', phone: '555-1212', age: 10 },
30013 { name: 'Mary', phone: '555-9876', age: 19 },
30014 { name: 'Mike', phone: '555-4321', age: 21 },
30015 { name: 'Adam', phone: '555-5678', age: 35 },
30016 { name: 'Julie', phone: '555-8765', age: 29 }
30018 $scope.order = function(predicate) {
30019 $scope.predicate = predicate;
30020 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
30021 $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
30023 $scope.order('age', true);
30027 <file name="style.css">
30031 .sortorder.reverse:after {
30037 orderByFilter.$inject = ['$parse'];
30038 function orderByFilter($parse) {
30039 return function(array, sortPredicate, reverseOrder) {
30041 if (array == null) return array;
30042 if (!isArrayLike(array)) {
30043 throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
30046 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
30047 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
30049 var predicates = processPredicates(sortPredicate, reverseOrder);
30050 // Add a predicate at the end that evaluates to the element index. This makes the
30051 // sort stable as it works as a tie-breaker when all the input predicates cannot
30052 // distinguish between two elements.
30053 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
30055 // The next three lines are a version of a Swartzian Transform idiom from Perl
30056 // (sometimes called the Decorate-Sort-Undecorate idiom)
30057 // See https://en.wikipedia.org/wiki/Schwartzian_transform
30058 var compareValues = Array.prototype.map.call(array, getComparisonObject);
30059 compareValues.sort(doComparison);
30060 array = compareValues.map(function(item) { return item.value; });
30064 function getComparisonObject(value, index) {
30067 predicateValues: predicates.map(function(predicate) {
30068 return getPredicateValue(predicate.get(value), index);
30073 function doComparison(v1, v2) {
30075 for (var index=0, length = predicates.length; index < length; ++index) {
30076 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
30083 function processPredicates(sortPredicate, reverseOrder) {
30084 reverseOrder = reverseOrder ? -1 : 1;
30085 return sortPredicate.map(function(predicate) {
30086 var descending = 1, get = identity;
30088 if (isFunction(predicate)) {
30090 } else if (isString(predicate)) {
30091 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
30092 descending = predicate.charAt(0) == '-' ? -1 : 1;
30093 predicate = predicate.substring(1);
30095 if (predicate !== '') {
30096 get = $parse(predicate);
30097 if (get.constant) {
30099 get = function(value) { return value[key]; };
30103 return { get: get, descending: descending * reverseOrder };
30107 function isPrimitive(value) {
30108 switch (typeof value) {
30109 case 'number': /* falls through */
30110 case 'boolean': /* falls through */
30118 function objectValue(value, index) {
30119 // If `valueOf` is a valid function use that
30120 if (typeof value.valueOf === 'function') {
30121 value = value.valueOf();
30122 if (isPrimitive(value)) return value;
30124 // If `toString` is a valid function and not the one from `Object.prototype` use that
30125 if (hasCustomToString(value)) {
30126 value = value.toString();
30127 if (isPrimitive(value)) return value;
30129 // We have a basic object so we use the position of the object in the collection
30133 function getPredicateValue(value, index) {
30134 var type = typeof value;
30135 if (value === null) {
30138 } else if (type === 'string') {
30139 value = value.toLowerCase();
30140 } else if (type === 'object') {
30141 value = objectValue(value, index);
30143 return { value: value, type: type };
30146 function compare(v1, v2) {
30148 if (v1.type === v2.type) {
30149 if (v1.value !== v2.value) {
30150 result = v1.value < v2.value ? -1 : 1;
30153 result = v1.type < v2.type ? -1 : 1;
30159 function ngDirective(directive) {
30160 if (isFunction(directive)) {
30165 directive.restrict = directive.restrict || 'AC';
30166 return valueFn(directive);
30175 * Modifies the default behavior of the html A tag so that the default action is prevented when
30176 * the href attribute is empty.
30178 * This change permits the easy creation of action links with the `ngClick` directive
30179 * without changing the location or causing page reloads, e.g.:
30180 * `<a href="" ng-click="list.addItem()">Add Item</a>`
30182 var htmlAnchorDirective = valueFn({
30184 compile: function(element, attr) {
30185 if (!attr.href && !attr.xlinkHref) {
30186 return function(scope, element) {
30187 // If the linked element is not an anchor tag anymore, do nothing
30188 if (element[0].nodeName.toLowerCase() !== 'a') return;
30190 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
30191 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
30192 'xlink:href' : 'href';
30193 element.on('click', function(event) {
30194 // if we have no href url, then don't navigate anywhere.
30195 if (!element.attr(href)) {
30196 event.preventDefault();
30211 * Using Angular markup like `{{hash}}` in an href attribute will
30212 * make the link go to the wrong URL if the user clicks it before
30213 * Angular has a chance to replace the `{{hash}}` markup with its
30214 * value. Until Angular replaces the markup the link will be broken
30215 * and will most likely return a 404 error. The `ngHref` directive
30216 * solves this problem.
30218 * The wrong way to write it:
30220 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
30223 * The correct way to write it:
30225 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
30229 * @param {template} ngHref any string which can contain `{{}}` markup.
30232 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
30233 * in links and their different behaviors:
30235 <file name="index.html">
30236 <input ng-model="value" /><br />
30237 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
30238 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
30239 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
30240 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
30241 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
30242 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
30244 <file name="protractor.js" type="protractor">
30245 it('should execute ng-click but not reload when href without value', function() {
30246 element(by.id('link-1')).click();
30247 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
30248 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
30251 it('should execute ng-click but not reload when href empty string', function() {
30252 element(by.id('link-2')).click();
30253 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
30254 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
30257 it('should execute ng-click and change url when ng-href specified', function() {
30258 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
30260 element(by.id('link-3')).click();
30262 // At this point, we navigate away from an Angular page, so we need
30263 // to use browser.driver to get the base webdriver.
30265 browser.wait(function() {
30266 return browser.driver.getCurrentUrl().then(function(url) {
30267 return url.match(/\/123$/);
30269 }, 5000, 'page should navigate to /123');
30272 it('should execute ng-click but not reload when href empty string and name specified', function() {
30273 element(by.id('link-4')).click();
30274 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
30275 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
30278 it('should execute ng-click but not reload when no href but name specified', function() {
30279 element(by.id('link-5')).click();
30280 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
30281 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
30284 it('should only change url when only ng-href', function() {
30285 element(by.model('value')).clear();
30286 element(by.model('value')).sendKeys('6');
30287 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
30289 element(by.id('link-6')).click();
30291 // At this point, we navigate away from an Angular page, so we need
30292 // to use browser.driver to get the base webdriver.
30293 browser.wait(function() {
30294 return browser.driver.getCurrentUrl().then(function(url) {
30295 return url.match(/\/6$/);
30297 }, 5000, 'page should navigate to /6');
30310 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
30311 * work right: The browser will fetch from the URL with the literal
30312 * text `{{hash}}` until Angular replaces the expression inside
30313 * `{{hash}}`. The `ngSrc` directive solves this problem.
30315 * The buggy way to write it:
30317 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
30320 * The correct way to write it:
30322 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
30326 * @param {template} ngSrc any string which can contain `{{}}` markup.
30336 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
30337 * work right: The browser will fetch from the URL with the literal
30338 * text `{{hash}}` until Angular replaces the expression inside
30339 * `{{hash}}`. The `ngSrcset` directive solves this problem.
30341 * The buggy way to write it:
30343 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
30346 * The correct way to write it:
30348 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
30352 * @param {template} ngSrcset any string which can contain `{{}}` markup.
30363 * This directive sets the `disabled` attribute on the element if the
30364 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
30366 * A special directive is necessary because we cannot use interpolation inside the `disabled`
30367 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30371 <file name="index.html">
30372 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
30373 <button ng-model="button" ng-disabled="checked">Button</button>
30375 <file name="protractor.js" type="protractor">
30376 it('should toggle button', function() {
30377 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
30378 element(by.model('checked')).click();
30379 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
30385 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
30386 * then the `disabled` attribute will be set on the element
30397 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
30399 * Note that this directive should not be used together with {@link ngModel `ngModel`},
30400 * as this can lead to unexpected behavior.
30402 * A special directive is necessary because we cannot use interpolation inside the `checked`
30403 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30407 <file name="index.html">
30408 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
30409 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
30411 <file name="protractor.js" type="protractor">
30412 it('should check both checkBoxes', function() {
30413 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
30414 element(by.model('master')).click();
30415 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
30421 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
30422 * then the `checked` attribute will be set on the element
30434 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
30436 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
30437 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30441 <file name="index.html">
30442 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
30443 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
30445 <file name="protractor.js" type="protractor">
30446 it('should toggle readonly attr', function() {
30447 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
30448 element(by.model('checked')).click();
30449 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
30455 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
30456 * then special attribute "readonly" will be set on the element
30468 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
30470 * A special directive is necessary because we cannot use interpolation inside the `selected`
30471 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30475 <file name="index.html">
30476 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
30477 <select aria-label="ngSelected demo">
30478 <option>Hello!</option>
30479 <option id="greet" ng-selected="selected">Greetings!</option>
30482 <file name="protractor.js" type="protractor">
30483 it('should select Greetings!', function() {
30484 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
30485 element(by.model('selected')).click();
30486 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
30492 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
30493 * then special attribute "selected" will be set on the element
30504 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
30506 * A special directive is necessary because we cannot use interpolation inside the `open`
30507 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30511 <file name="index.html">
30512 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
30513 <details id="details" ng-open="open">
30514 <summary>Show/Hide me</summary>
30517 <file name="protractor.js" type="protractor">
30518 it('should toggle open', function() {
30519 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
30520 element(by.model('open')).click();
30521 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
30527 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
30528 * then special attribute "open" will be set on the element
30531 var ngAttributeAliasDirectives = {};
30533 // boolean attrs are evaluated
30534 forEach(BOOLEAN_ATTR, function(propName, attrName) {
30535 // binding to multiple is not supported
30536 if (propName == "multiple") return;
30538 function defaultLinkFn(scope, element, attr) {
30539 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
30540 attr.$set(attrName, !!value);
30544 var normalized = directiveNormalize('ng-' + attrName);
30545 var linkFn = defaultLinkFn;
30547 if (propName === 'checked') {
30548 linkFn = function(scope, element, attr) {
30549 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
30550 if (attr.ngModel !== attr[normalized]) {
30551 defaultLinkFn(scope, element, attr);
30556 ngAttributeAliasDirectives[normalized] = function() {
30565 // aliased input attrs are evaluated
30566 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
30567 ngAttributeAliasDirectives[ngAttr] = function() {
30570 link: function(scope, element, attr) {
30571 //special case ngPattern when a literal regular expression value
30572 //is used as the expression (this way we don't have to watch anything).
30573 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
30574 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
30576 attr.$set("ngPattern", new RegExp(match[1], match[2]));
30581 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
30582 attr.$set(ngAttr, value);
30589 // ng-src, ng-srcset, ng-href are interpolated
30590 forEach(['src', 'srcset', 'href'], function(attrName) {
30591 var normalized = directiveNormalize('ng-' + attrName);
30592 ngAttributeAliasDirectives[normalized] = function() {
30594 priority: 99, // it needs to run after the attributes are interpolated
30595 link: function(scope, element, attr) {
30596 var propName = attrName,
30599 if (attrName === 'href' &&
30600 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
30601 name = 'xlinkHref';
30602 attr.$attr[name] = 'xlink:href';
30606 attr.$observe(normalized, function(value) {
30608 if (attrName === 'href') {
30609 attr.$set(name, null);
30614 attr.$set(name, value);
30616 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
30617 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
30618 // to set the property as well to achieve the desired effect.
30619 // we use attr[attrName] value since $set can sanitize the url.
30620 if (msie && propName) element.prop(propName, attr[name]);
30627 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
30629 var nullFormCtrl = {
30631 $$renameControl: nullFormRenameControl,
30632 $removeControl: noop,
30633 $setValidity: noop,
30635 $setPristine: noop,
30636 $setSubmitted: noop
30638 SUBMITTED_CLASS = 'ng-submitted';
30640 function nullFormRenameControl(control, name) {
30641 control.$name = name;
30646 * @name form.FormController
30648 * @property {boolean} $pristine True if user has not interacted with the form yet.
30649 * @property {boolean} $dirty True if user has already interacted with the form.
30650 * @property {boolean} $valid True if all of the containing forms and controls are valid.
30651 * @property {boolean} $invalid True if at least one containing control or form is invalid.
30652 * @property {boolean} $pending True if at least one containing control or form is pending.
30653 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
30655 * @property {Object} $error Is an object hash, containing references to controls or
30656 * forms with failing validators, where:
30658 * - keys are validation tokens (error names),
30659 * - values are arrays of controls or forms that have a failing validator for given error name.
30661 * Built-in validation tokens:
30673 * - `datetimelocal`
30679 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
30680 * such as being valid/invalid or dirty/pristine.
30682 * Each {@link ng.directive:form form} directive creates an instance
30683 * of `FormController`.
30686 //asks for $scope to fool the BC controller module
30687 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
30688 function FormController(element, attrs, $scope, $animate, $interpolate) {
30694 form.$$success = {};
30695 form.$pending = undefined;
30696 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
30697 form.$dirty = false;
30698 form.$pristine = true;
30699 form.$valid = true;
30700 form.$invalid = false;
30701 form.$submitted = false;
30702 form.$$parentForm = nullFormCtrl;
30706 * @name form.FormController#$rollbackViewValue
30709 * Rollback all form controls pending updates to the `$modelValue`.
30711 * Updates may be pending by a debounced event or because the input is waiting for a some future
30712 * event defined in `ng-model-options`. This method is typically needed by the reset button of
30713 * a form that uses `ng-model-options` to pend updates.
30715 form.$rollbackViewValue = function() {
30716 forEach(controls, function(control) {
30717 control.$rollbackViewValue();
30723 * @name form.FormController#$commitViewValue
30726 * Commit all form controls pending updates to the `$modelValue`.
30728 * Updates may be pending by a debounced event or because the input is waiting for a some future
30729 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
30730 * usually handles calling this in response to input events.
30732 form.$commitViewValue = function() {
30733 forEach(controls, function(control) {
30734 control.$commitViewValue();
30740 * @name form.FormController#$addControl
30741 * @param {object} control control object, either a {@link form.FormController} or an
30742 * {@link ngModel.NgModelController}
30745 * Register a control with the form. Input elements using ngModelController do this automatically
30746 * when they are linked.
30748 * Note that the current state of the control will not be reflected on the new parent form. This
30749 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
30752 * However, if the method is used programmatically, for example by adding dynamically created controls,
30753 * or controls that have been previously removed without destroying their corresponding DOM element,
30754 * it's the developers responsibility to make sure the current state propagates to the parent form.
30756 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
30757 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
30759 form.$addControl = function(control) {
30760 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
30761 // and not added to the scope. Now we throw an error.
30762 assertNotHasOwnProperty(control.$name, 'input');
30763 controls.push(control);
30765 if (control.$name) {
30766 form[control.$name] = control;
30769 control.$$parentForm = form;
30772 // Private API: rename a form control
30773 form.$$renameControl = function(control, newName) {
30774 var oldName = control.$name;
30776 if (form[oldName] === control) {
30777 delete form[oldName];
30779 form[newName] = control;
30780 control.$name = newName;
30785 * @name form.FormController#$removeControl
30786 * @param {object} control control object, either a {@link form.FormController} or an
30787 * {@link ngModel.NgModelController}
30790 * Deregister a control from the form.
30792 * Input elements using ngModelController do this automatically when they are destroyed.
30794 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
30795 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
30796 * different from case to case. For example, removing the only `$dirty` control from a form may or
30797 * may not mean that the form is still `$dirty`.
30799 form.$removeControl = function(control) {
30800 if (control.$name && form[control.$name] === control) {
30801 delete form[control.$name];
30803 forEach(form.$pending, function(value, name) {
30804 form.$setValidity(name, null, control);
30806 forEach(form.$error, function(value, name) {
30807 form.$setValidity(name, null, control);
30809 forEach(form.$$success, function(value, name) {
30810 form.$setValidity(name, null, control);
30813 arrayRemove(controls, control);
30814 control.$$parentForm = nullFormCtrl;
30820 * @name form.FormController#$setValidity
30823 * Sets the validity of a form control.
30825 * This method will also propagate to parent forms.
30827 addSetValidityMethod({
30830 set: function(object, property, controller) {
30831 var list = object[property];
30833 object[property] = [controller];
30835 var index = list.indexOf(controller);
30836 if (index === -1) {
30837 list.push(controller);
30841 unset: function(object, property, controller) {
30842 var list = object[property];
30846 arrayRemove(list, controller);
30847 if (list.length === 0) {
30848 delete object[property];
30856 * @name form.FormController#$setDirty
30859 * Sets the form to a dirty state.
30861 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
30862 * state (ng-dirty class). This method will also propagate to parent forms.
30864 form.$setDirty = function() {
30865 $animate.removeClass(element, PRISTINE_CLASS);
30866 $animate.addClass(element, DIRTY_CLASS);
30867 form.$dirty = true;
30868 form.$pristine = false;
30869 form.$$parentForm.$setDirty();
30874 * @name form.FormController#$setPristine
30877 * Sets the form to its pristine state.
30879 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
30880 * state (ng-pristine class). This method will also propagate to all the controls contained
30883 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
30884 * saving or resetting it.
30886 form.$setPristine = function() {
30887 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
30888 form.$dirty = false;
30889 form.$pristine = true;
30890 form.$submitted = false;
30891 forEach(controls, function(control) {
30892 control.$setPristine();
30898 * @name form.FormController#$setUntouched
30901 * Sets the form to its untouched state.
30903 * This method can be called to remove the 'ng-touched' class and set the form controls to their
30904 * untouched state (ng-untouched class).
30906 * Setting a form controls back to their untouched state is often useful when setting the form
30907 * back to its pristine state.
30909 form.$setUntouched = function() {
30910 forEach(controls, function(control) {
30911 control.$setUntouched();
30917 * @name form.FormController#$setSubmitted
30920 * Sets the form to its submitted state.
30922 form.$setSubmitted = function() {
30923 $animate.addClass(element, SUBMITTED_CLASS);
30924 form.$submitted = true;
30925 form.$$parentForm.$setSubmitted();
30935 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
30936 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
30937 * sub-group of controls needs to be determined.
30939 * Note: the purpose of `ngForm` is to group controls,
30940 * but not to be a replacement for the `<form>` tag with all of its capabilities
30941 * (e.g. posting to the server, ...).
30943 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
30944 * related scope, under this name.
30954 * Directive that instantiates
30955 * {@link form.FormController FormController}.
30957 * If the `name` attribute is specified, the form controller is published onto the current scope under
30960 * # Alias: {@link ng.directive:ngForm `ngForm`}
30962 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
30963 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
30964 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
30965 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
30966 * of controls needs to be determined.
30969 * - `ng-valid` is set if the form is valid.
30970 * - `ng-invalid` is set if the form is invalid.
30971 * - `ng-pending` is set if the form is pending.
30972 * - `ng-pristine` is set if the form is pristine.
30973 * - `ng-dirty` is set if the form is dirty.
30974 * - `ng-submitted` is set if the form was submitted.
30976 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
30979 * # Submitting a form and preventing the default action
30981 * Since the role of forms in client-side Angular applications is different than in classical
30982 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
30983 * page reload that sends the data to the server. Instead some javascript logic should be triggered
30984 * to handle the form submission in an application-specific way.
30986 * For this reason, Angular prevents the default action (form submission to the server) unless the
30987 * `<form>` element has an `action` attribute specified.
30989 * You can use one of the following two ways to specify what javascript method should be called when
30990 * a form is submitted:
30992 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
30993 * - {@link ng.directive:ngClick ngClick} directive on the first
30994 * button or input field of type submit (input[type=submit])
30996 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
30997 * or {@link ng.directive:ngClick ngClick} directives.
30998 * This is because of the following form submission rules in the HTML specification:
31000 * - If a form has only one input field then hitting enter in this field triggers form submit
31002 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
31003 * doesn't trigger submit
31004 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
31005 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
31006 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
31008 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
31009 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
31010 * to have access to the updated model.
31012 * ## Animation Hooks
31014 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
31015 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
31016 * other validations that are performed within the form. Animations in ngForm are similar to how
31017 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
31018 * as JS animations.
31020 * The following example shows a simple way to utilize CSS transitions to style a form element
31021 * that has been rendered as invalid after it has been validated:
31024 * //be sure to include ngAnimate as a module to hook into more
31025 * //advanced animations
31027 * transition:0.5s linear all;
31028 * background: white;
31030 * .my-form.ng-invalid {
31037 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
31038 <file name="index.html">
31040 angular.module('formExample', [])
31041 .controller('FormController', ['$scope', function($scope) {
31042 $scope.userType = 'guest';
31047 transition:all linear 0.5s;
31048 background: transparent;
31050 .my-form.ng-invalid {
31054 <form name="myForm" ng-controller="FormController" class="my-form">
31055 userType: <input name="input" ng-model="userType" required>
31056 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
31057 <code>userType = {{userType}}</code><br>
31058 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
31059 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
31060 <code>myForm.$valid = {{myForm.$valid}}</code><br>
31061 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
31064 <file name="protractor.js" type="protractor">
31065 it('should initialize to model', function() {
31066 var userType = element(by.binding('userType'));
31067 var valid = element(by.binding('myForm.input.$valid'));
31069 expect(userType.getText()).toContain('guest');
31070 expect(valid.getText()).toContain('true');
31073 it('should be invalid if empty', function() {
31074 var userType = element(by.binding('userType'));
31075 var valid = element(by.binding('myForm.input.$valid'));
31076 var userInput = element(by.model('userType'));
31079 userInput.sendKeys('');
31081 expect(userType.getText()).toEqual('userType =');
31082 expect(valid.getText()).toContain('false');
31087 * @param {string=} name Name of the form. If specified, the form controller will be published into
31088 * related scope, under this name.
31090 var formDirectiveFactory = function(isNgForm) {
31091 return ['$timeout', '$parse', function($timeout, $parse) {
31092 var formDirective = {
31094 restrict: isNgForm ? 'EAC' : 'E',
31095 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
31096 controller: FormController,
31097 compile: function ngFormCompile(formElement, attr) {
31098 // Setup initial state of the control
31099 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
31101 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
31104 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
31105 var controller = ctrls[0];
31107 // if `action` attr is not present on the form, prevent the default action (submission)
31108 if (!('action' in attr)) {
31109 // we can't use jq events because if a form is destroyed during submission the default
31110 // action is not prevented. see #1238
31112 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
31113 // page reload if the form was destroyed by submission of the form via a click handler
31114 // on a button in the form. Looks like an IE9 specific bug.
31115 var handleFormSubmission = function(event) {
31116 scope.$apply(function() {
31117 controller.$commitViewValue();
31118 controller.$setSubmitted();
31121 event.preventDefault();
31124 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
31126 // unregister the preventDefault listener so that we don't not leak memory but in a
31127 // way that will achieve the prevention of the default action.
31128 formElement.on('$destroy', function() {
31129 $timeout(function() {
31130 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
31135 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
31136 parentFormCtrl.$addControl(controller);
31138 var setter = nameAttr ? getSetter(controller.$name) : noop;
31141 setter(scope, controller);
31142 attr.$observe(nameAttr, function(newValue) {
31143 if (controller.$name === newValue) return;
31144 setter(scope, undefined);
31145 controller.$$parentForm.$$renameControl(controller, newValue);
31146 setter = getSetter(controller.$name);
31147 setter(scope, controller);
31150 formElement.on('$destroy', function() {
31151 controller.$$parentForm.$removeControl(controller);
31152 setter(scope, undefined);
31153 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
31160 return formDirective;
31162 function getSetter(expression) {
31163 if (expression === '') {
31164 //create an assignable expression, so forms with an empty name can be renamed later
31165 return $parse('this[""]').assign;
31167 return $parse(expression).assign || noop;
31172 var formDirective = formDirectiveFactory();
31173 var ngFormDirective = formDirectiveFactory(true);
31175 /* global VALID_CLASS: false,
31176 INVALID_CLASS: false,
31177 PRISTINE_CLASS: false,
31178 DIRTY_CLASS: false,
31179 UNTOUCHED_CLASS: false,
31180 TOUCHED_CLASS: false,
31181 ngModelMinErr: false,
31184 // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
31185 var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
31186 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
31187 // Note: We are being more lenient, because browsers are too.
31197 // 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
31198 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
31199 var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
31200 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
31201 var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
31202 var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
31203 var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
31204 var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
31205 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
31207 var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
31208 var PARTIAL_VALIDATION_TYPES = createMap();
31209 forEach('date,datetime-local,month,time,week'.split(','), function(type) {
31210 PARTIAL_VALIDATION_TYPES[type] = true;
31217 * @name input[text]
31220 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
31223 * @param {string} ngModel Assignable angular expression to data-bind to.
31224 * @param {string=} name Property name of the form under which the control is published.
31225 * @param {string=} required Adds `required` validation error key if the value is not entered.
31226 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31227 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31228 * `required` when you want to data-bind to the `required` attribute.
31229 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31231 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31232 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
31234 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
31235 * that contains the regular expression body that will be converted to a regular expression
31236 * as in the ngPattern directive.
31237 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
31238 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
31239 * If the expression evaluates to a RegExp object, then this is used directly.
31240 * If the expression evaluates to a string, then it will be converted to a RegExp
31241 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31242 * `new RegExp('^abc$')`.<br />
31243 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31244 * start at the index of the last search's match, thus not taking the whole input value into
31246 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31247 * interaction with the input element.
31248 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
31249 * This parameter is ignored for input[type=password] controls, which will never trim the
31253 <example name="text-input-directive" module="textInputExample">
31254 <file name="index.html">
31256 angular.module('textInputExample', [])
31257 .controller('ExampleController', ['$scope', function($scope) {
31260 word: /^\s*\w*\s*$/
31264 <form name="myForm" ng-controller="ExampleController">
31265 <label>Single word:
31266 <input type="text" name="input" ng-model="example.text"
31267 ng-pattern="example.word" required ng-trim="false">
31270 <span class="error" ng-show="myForm.input.$error.required">
31272 <span class="error" ng-show="myForm.input.$error.pattern">
31273 Single word only!</span>
31275 <tt>text = {{example.text}}</tt><br/>
31276 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31277 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31278 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31279 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31282 <file name="protractor.js" type="protractor">
31283 var text = element(by.binding('example.text'));
31284 var valid = element(by.binding('myForm.input.$valid'));
31285 var input = element(by.model('example.text'));
31287 it('should initialize to model', function() {
31288 expect(text.getText()).toContain('guest');
31289 expect(valid.getText()).toContain('true');
31292 it('should be invalid if empty', function() {
31294 input.sendKeys('');
31296 expect(text.getText()).toEqual('text =');
31297 expect(valid.getText()).toContain('false');
31300 it('should be invalid if multi word', function() {
31302 input.sendKeys('hello world');
31304 expect(valid.getText()).toContain('false');
31309 'text': textInputType,
31313 * @name input[date]
31316 * Input with date validation and transformation. In browsers that do not yet support
31317 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
31318 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
31319 * modern browsers do not yet support this input type, it is important to provide cues to users on the
31320 * expected input format via a placeholder or label.
31322 * The model must always be a Date object, otherwise Angular will throw an error.
31323 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31325 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31326 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31328 * @param {string} ngModel Assignable angular expression to data-bind to.
31329 * @param {string=} name Property name of the form under which the control is published.
31330 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
31331 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
31332 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
31333 * constraint validation.
31334 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
31335 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
31336 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
31337 * constraint validation.
31338 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
31339 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31340 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
31341 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31342 * @param {string=} required Sets `required` validation error key if the value is not entered.
31343 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31344 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31345 * `required` when you want to data-bind to the `required` attribute.
31346 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31347 * interaction with the input element.
31350 <example name="date-input-directive" module="dateInputExample">
31351 <file name="index.html">
31353 angular.module('dateInputExample', [])
31354 .controller('DateController', ['$scope', function($scope) {
31356 value: new Date(2013, 9, 22)
31360 <form name="myForm" ng-controller="DateController as dateCtrl">
31361 <label for="exampleInput">Pick a date in 2013:</label>
31362 <input type="date" id="exampleInput" name="input" ng-model="example.value"
31363 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
31365 <span class="error" ng-show="myForm.input.$error.required">
31367 <span class="error" ng-show="myForm.input.$error.date">
31368 Not a valid date!</span>
31370 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
31371 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31372 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31373 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31374 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31377 <file name="protractor.js" type="protractor">
31378 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
31379 var valid = element(by.binding('myForm.input.$valid'));
31380 var input = element(by.model('example.value'));
31382 // currently protractor/webdriver does not support
31383 // sending keys to all known HTML5 input controls
31384 // for various browsers (see https://github.com/angular/protractor/issues/562).
31385 function setInput(val) {
31386 // set the value of the element and force validation.
31387 var scr = "var ipt = document.getElementById('exampleInput'); " +
31388 "ipt.value = '" + val + "';" +
31389 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31390 browser.executeScript(scr);
31393 it('should initialize to model', function() {
31394 expect(value.getText()).toContain('2013-10-22');
31395 expect(valid.getText()).toContain('myForm.input.$valid = true');
31398 it('should be invalid if empty', function() {
31400 expect(value.getText()).toEqual('value =');
31401 expect(valid.getText()).toContain('myForm.input.$valid = false');
31404 it('should be invalid if over max', function() {
31405 setInput('2015-01-01');
31406 expect(value.getText()).toContain('');
31407 expect(valid.getText()).toContain('myForm.input.$valid = false');
31412 'date': createDateInputType('date', DATE_REGEXP,
31413 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
31418 * @name input[datetime-local]
31421 * Input with datetime validation and transformation. In browsers that do not yet support
31422 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
31423 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
31425 * The model must always be a Date object, otherwise Angular will throw an error.
31426 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31428 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31429 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31431 * @param {string} ngModel Assignable angular expression to data-bind to.
31432 * @param {string=} name Property name of the form under which the control is published.
31433 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31434 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
31435 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
31436 * Note that `min` will also add native HTML5 constraint validation.
31437 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31438 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
31439 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
31440 * Note that `max` will also add native HTML5 constraint validation.
31441 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
31442 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31443 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
31444 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31445 * @param {string=} required Sets `required` validation error key if the value is not entered.
31446 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31447 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31448 * `required` when you want to data-bind to the `required` attribute.
31449 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31450 * interaction with the input element.
31453 <example name="datetimelocal-input-directive" module="dateExample">
31454 <file name="index.html">
31456 angular.module('dateExample', [])
31457 .controller('DateController', ['$scope', function($scope) {
31459 value: new Date(2010, 11, 28, 14, 57)
31463 <form name="myForm" ng-controller="DateController as dateCtrl">
31464 <label for="exampleInput">Pick a date between in 2013:</label>
31465 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
31466 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
31468 <span class="error" ng-show="myForm.input.$error.required">
31470 <span class="error" ng-show="myForm.input.$error.datetimelocal">
31471 Not a valid date!</span>
31473 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
31474 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31475 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31476 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31477 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31480 <file name="protractor.js" type="protractor">
31481 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
31482 var valid = element(by.binding('myForm.input.$valid'));
31483 var input = element(by.model('example.value'));
31485 // currently protractor/webdriver does not support
31486 // sending keys to all known HTML5 input controls
31487 // for various browsers (https://github.com/angular/protractor/issues/562).
31488 function setInput(val) {
31489 // set the value of the element and force validation.
31490 var scr = "var ipt = document.getElementById('exampleInput'); " +
31491 "ipt.value = '" + val + "';" +
31492 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31493 browser.executeScript(scr);
31496 it('should initialize to model', function() {
31497 expect(value.getText()).toContain('2010-12-28T14:57:00');
31498 expect(valid.getText()).toContain('myForm.input.$valid = true');
31501 it('should be invalid if empty', function() {
31503 expect(value.getText()).toEqual('value =');
31504 expect(valid.getText()).toContain('myForm.input.$valid = false');
31507 it('should be invalid if over max', function() {
31508 setInput('2015-01-01T23:59:00');
31509 expect(value.getText()).toContain('');
31510 expect(valid.getText()).toContain('myForm.input.$valid = false');
31515 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
31516 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
31517 'yyyy-MM-ddTHH:mm:ss.sss'),
31521 * @name input[time]
31524 * Input with time validation and transformation. In browsers that do not yet support
31525 * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
31526 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
31527 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
31529 * The model must always be a Date object, otherwise Angular will throw an error.
31530 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31532 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31533 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31535 * @param {string} ngModel Assignable angular expression to data-bind to.
31536 * @param {string=} name Property name of the form under which the control is published.
31537 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31538 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
31539 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
31540 * native HTML5 constraint validation.
31541 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31542 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
31543 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
31544 * native HTML5 constraint validation.
31545 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
31546 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31547 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
31548 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31549 * @param {string=} required Sets `required` validation error key if the value is not entered.
31550 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31551 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31552 * `required` when you want to data-bind to the `required` attribute.
31553 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31554 * interaction with the input element.
31557 <example name="time-input-directive" module="timeExample">
31558 <file name="index.html">
31560 angular.module('timeExample', [])
31561 .controller('DateController', ['$scope', function($scope) {
31563 value: new Date(1970, 0, 1, 14, 57, 0)
31567 <form name="myForm" ng-controller="DateController as dateCtrl">
31568 <label for="exampleInput">Pick a time between 8am and 5pm:</label>
31569 <input type="time" id="exampleInput" name="input" ng-model="example.value"
31570 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
31572 <span class="error" ng-show="myForm.input.$error.required">
31574 <span class="error" ng-show="myForm.input.$error.time">
31575 Not a valid date!</span>
31577 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
31578 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31579 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31580 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31581 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31584 <file name="protractor.js" type="protractor">
31585 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
31586 var valid = element(by.binding('myForm.input.$valid'));
31587 var input = element(by.model('example.value'));
31589 // currently protractor/webdriver does not support
31590 // sending keys to all known HTML5 input controls
31591 // for various browsers (https://github.com/angular/protractor/issues/562).
31592 function setInput(val) {
31593 // set the value of the element and force validation.
31594 var scr = "var ipt = document.getElementById('exampleInput'); " +
31595 "ipt.value = '" + val + "';" +
31596 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31597 browser.executeScript(scr);
31600 it('should initialize to model', function() {
31601 expect(value.getText()).toContain('14:57:00');
31602 expect(valid.getText()).toContain('myForm.input.$valid = true');
31605 it('should be invalid if empty', function() {
31607 expect(value.getText()).toEqual('value =');
31608 expect(valid.getText()).toContain('myForm.input.$valid = false');
31611 it('should be invalid if over max', function() {
31612 setInput('23:59:00');
31613 expect(value.getText()).toContain('');
31614 expect(valid.getText()).toContain('myForm.input.$valid = false');
31619 'time': createDateInputType('time', TIME_REGEXP,
31620 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
31625 * @name input[week]
31628 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
31629 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
31630 * week format (yyyy-W##), for example: `2013-W02`.
31632 * The model must always be a Date object, otherwise Angular will throw an error.
31633 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31635 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31636 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31638 * @param {string} ngModel Assignable angular expression to data-bind to.
31639 * @param {string=} name Property name of the form under which the control is published.
31640 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31641 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
31642 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
31643 * native HTML5 constraint validation.
31644 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31645 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
31646 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
31647 * native HTML5 constraint validation.
31648 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
31649 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31650 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
31651 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31652 * @param {string=} required Sets `required` validation error key if the value is not entered.
31653 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31654 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31655 * `required` when you want to data-bind to the `required` attribute.
31656 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31657 * interaction with the input element.
31660 <example name="week-input-directive" module="weekExample">
31661 <file name="index.html">
31663 angular.module('weekExample', [])
31664 .controller('DateController', ['$scope', function($scope) {
31666 value: new Date(2013, 0, 3)
31670 <form name="myForm" ng-controller="DateController as dateCtrl">
31671 <label>Pick a date between in 2013:
31672 <input id="exampleInput" type="week" name="input" ng-model="example.value"
31673 placeholder="YYYY-W##" min="2012-W32"
31674 max="2013-W52" required />
31677 <span class="error" ng-show="myForm.input.$error.required">
31679 <span class="error" ng-show="myForm.input.$error.week">
31680 Not a valid date!</span>
31682 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
31683 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31684 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31685 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31686 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31689 <file name="protractor.js" type="protractor">
31690 var value = element(by.binding('example.value | date: "yyyy-Www"'));
31691 var valid = element(by.binding('myForm.input.$valid'));
31692 var input = element(by.model('example.value'));
31694 // currently protractor/webdriver does not support
31695 // sending keys to all known HTML5 input controls
31696 // for various browsers (https://github.com/angular/protractor/issues/562).
31697 function setInput(val) {
31698 // set the value of the element and force validation.
31699 var scr = "var ipt = document.getElementById('exampleInput'); " +
31700 "ipt.value = '" + val + "';" +
31701 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31702 browser.executeScript(scr);
31705 it('should initialize to model', function() {
31706 expect(value.getText()).toContain('2013-W01');
31707 expect(valid.getText()).toContain('myForm.input.$valid = true');
31710 it('should be invalid if empty', function() {
31712 expect(value.getText()).toEqual('value =');
31713 expect(valid.getText()).toContain('myForm.input.$valid = false');
31716 it('should be invalid if over max', function() {
31717 setInput('2015-W01');
31718 expect(value.getText()).toContain('');
31719 expect(valid.getText()).toContain('myForm.input.$valid = false');
31724 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
31728 * @name input[month]
31731 * Input with month validation and transformation. In browsers that do not yet support
31732 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
31733 * month format (yyyy-MM), for example: `2009-01`.
31735 * The model must always be a Date object, otherwise Angular will throw an error.
31736 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31737 * If the model is not set to the first of the month, the next view to model update will set it
31738 * to the first of the month.
31740 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31741 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31743 * @param {string} ngModel Assignable angular expression to data-bind to.
31744 * @param {string=} name Property name of the form under which the control is published.
31745 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31746 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
31747 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
31748 * native HTML5 constraint validation.
31749 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31750 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
31751 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
31752 * native HTML5 constraint validation.
31753 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
31754 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31755 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
31756 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31758 * @param {string=} required Sets `required` validation error key if the value is not entered.
31759 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31760 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31761 * `required` when you want to data-bind to the `required` attribute.
31762 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31763 * interaction with the input element.
31766 <example name="month-input-directive" module="monthExample">
31767 <file name="index.html">
31769 angular.module('monthExample', [])
31770 .controller('DateController', ['$scope', function($scope) {
31772 value: new Date(2013, 9, 1)
31776 <form name="myForm" ng-controller="DateController as dateCtrl">
31777 <label for="exampleInput">Pick a month in 2013:</label>
31778 <input id="exampleInput" type="month" name="input" ng-model="example.value"
31779 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
31781 <span class="error" ng-show="myForm.input.$error.required">
31783 <span class="error" ng-show="myForm.input.$error.month">
31784 Not a valid month!</span>
31786 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
31787 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31788 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31789 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31790 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31793 <file name="protractor.js" type="protractor">
31794 var value = element(by.binding('example.value | date: "yyyy-MM"'));
31795 var valid = element(by.binding('myForm.input.$valid'));
31796 var input = element(by.model('example.value'));
31798 // currently protractor/webdriver does not support
31799 // sending keys to all known HTML5 input controls
31800 // for various browsers (https://github.com/angular/protractor/issues/562).
31801 function setInput(val) {
31802 // set the value of the element and force validation.
31803 var scr = "var ipt = document.getElementById('exampleInput'); " +
31804 "ipt.value = '" + val + "';" +
31805 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31806 browser.executeScript(scr);
31809 it('should initialize to model', function() {
31810 expect(value.getText()).toContain('2013-10');
31811 expect(valid.getText()).toContain('myForm.input.$valid = true');
31814 it('should be invalid if empty', function() {
31816 expect(value.getText()).toEqual('value =');
31817 expect(valid.getText()).toContain('myForm.input.$valid = false');
31820 it('should be invalid if over max', function() {
31821 setInput('2015-01');
31822 expect(value.getText()).toContain('');
31823 expect(valid.getText()).toContain('myForm.input.$valid = false');
31828 'month': createDateInputType('month', MONTH_REGEXP,
31829 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
31834 * @name input[number]
31837 * Text input with number validation and transformation. Sets the `number` validation
31838 * error if not a valid number.
31840 * <div class="alert alert-warning">
31841 * The model must always be of type `number` otherwise Angular will throw an error.
31842 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
31843 * error docs for more information and an example of how to convert your model if necessary.
31846 * ## Issues with HTML5 constraint validation
31848 * In browsers that follow the
31849 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
31850 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
31851 * If a non-number is entered in the input, the browser will report the value as an empty string,
31852 * which means the view / model values in `ngModel` and subsequently the scope value
31853 * will also be an empty string.
31856 * @param {string} ngModel Assignable angular expression to data-bind to.
31857 * @param {string=} name Property name of the form under which the control is published.
31858 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31859 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31860 * @param {string=} required Sets `required` validation error key if the value is not entered.
31861 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31862 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31863 * `required` when you want to data-bind to the `required` attribute.
31864 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31866 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31867 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
31869 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
31870 * that contains the regular expression body that will be converted to a regular expression
31871 * as in the ngPattern directive.
31872 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
31873 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
31874 * If the expression evaluates to a RegExp object, then this is used directly.
31875 * If the expression evaluates to a string, then it will be converted to a RegExp
31876 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31877 * `new RegExp('^abc$')`.<br />
31878 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31879 * start at the index of the last search's match, thus not taking the whole input value into
31881 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31882 * interaction with the input element.
31885 <example name="number-input-directive" module="numberExample">
31886 <file name="index.html">
31888 angular.module('numberExample', [])
31889 .controller('ExampleController', ['$scope', function($scope) {
31895 <form name="myForm" ng-controller="ExampleController">
31897 <input type="number" name="input" ng-model="example.value"
31898 min="0" max="99" required>
31901 <span class="error" ng-show="myForm.input.$error.required">
31903 <span class="error" ng-show="myForm.input.$error.number">
31904 Not valid number!</span>
31906 <tt>value = {{example.value}}</tt><br/>
31907 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31908 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31909 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31910 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31913 <file name="protractor.js" type="protractor">
31914 var value = element(by.binding('example.value'));
31915 var valid = element(by.binding('myForm.input.$valid'));
31916 var input = element(by.model('example.value'));
31918 it('should initialize to model', function() {
31919 expect(value.getText()).toContain('12');
31920 expect(valid.getText()).toContain('true');
31923 it('should be invalid if empty', function() {
31925 input.sendKeys('');
31926 expect(value.getText()).toEqual('value =');
31927 expect(valid.getText()).toContain('false');
31930 it('should be invalid if over max', function() {
31932 input.sendKeys('123');
31933 expect(value.getText()).toEqual('value =');
31934 expect(valid.getText()).toContain('false');
31939 'number': numberInputType,
31947 * Text input with URL validation. Sets the `url` validation error key if the content is not a
31950 * <div class="alert alert-warning">
31951 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
31952 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
31953 * the built-in validators (see the {@link guide/forms Forms guide})
31956 * @param {string} ngModel Assignable angular expression to data-bind to.
31957 * @param {string=} name Property name of the form under which the control is published.
31958 * @param {string=} required Sets `required` validation error key if the value is not entered.
31959 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31960 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31961 * `required` when you want to data-bind to the `required` attribute.
31962 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31964 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31965 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
31967 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
31968 * that contains the regular expression body that will be converted to a regular expression
31969 * as in the ngPattern directive.
31970 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
31971 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
31972 * If the expression evaluates to a RegExp object, then this is used directly.
31973 * If the expression evaluates to a string, then it will be converted to a RegExp
31974 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31975 * `new RegExp('^abc$')`.<br />
31976 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31977 * start at the index of the last search's match, thus not taking the whole input value into
31979 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31980 * interaction with the input element.
31983 <example name="url-input-directive" module="urlExample">
31984 <file name="index.html">
31986 angular.module('urlExample', [])
31987 .controller('ExampleController', ['$scope', function($scope) {
31989 text: 'http://google.com'
31993 <form name="myForm" ng-controller="ExampleController">
31995 <input type="url" name="input" ng-model="url.text" required>
31998 <span class="error" ng-show="myForm.input.$error.required">
32000 <span class="error" ng-show="myForm.input.$error.url">
32001 Not valid url!</span>
32003 <tt>text = {{url.text}}</tt><br/>
32004 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
32005 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
32006 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
32007 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
32008 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
32011 <file name="protractor.js" type="protractor">
32012 var text = element(by.binding('url.text'));
32013 var valid = element(by.binding('myForm.input.$valid'));
32014 var input = element(by.model('url.text'));
32016 it('should initialize to model', function() {
32017 expect(text.getText()).toContain('http://google.com');
32018 expect(valid.getText()).toContain('true');
32021 it('should be invalid if empty', function() {
32023 input.sendKeys('');
32025 expect(text.getText()).toEqual('text =');
32026 expect(valid.getText()).toContain('false');
32029 it('should be invalid if not url', function() {
32031 input.sendKeys('box');
32033 expect(valid.getText()).toContain('false');
32038 'url': urlInputType,
32043 * @name input[email]
32046 * Text input with email validation. Sets the `email` validation error key if not a valid email
32049 * <div class="alert alert-warning">
32050 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
32051 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
32052 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
32055 * @param {string} ngModel Assignable angular expression to data-bind to.
32056 * @param {string=} name Property name of the form under which the control is published.
32057 * @param {string=} required Sets `required` validation error key if the value is not entered.
32058 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
32059 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
32060 * `required` when you want to data-bind to the `required` attribute.
32061 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
32063 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
32064 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
32066 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
32067 * that contains the regular expression body that will be converted to a regular expression
32068 * as in the ngPattern directive.
32069 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
32070 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
32071 * If the expression evaluates to a RegExp object, then this is used directly.
32072 * If the expression evaluates to a string, then it will be converted to a RegExp
32073 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
32074 * `new RegExp('^abc$')`.<br />
32075 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32076 * start at the index of the last search's match, thus not taking the whole input value into
32078 * @param {string=} ngChange Angular expression to be executed when input changes due to user
32079 * interaction with the input element.
32082 <example name="email-input-directive" module="emailExample">
32083 <file name="index.html">
32085 angular.module('emailExample', [])
32086 .controller('ExampleController', ['$scope', function($scope) {
32088 text: 'me@example.com'
32092 <form name="myForm" ng-controller="ExampleController">
32094 <input type="email" name="input" ng-model="email.text" required>
32097 <span class="error" ng-show="myForm.input.$error.required">
32099 <span class="error" ng-show="myForm.input.$error.email">
32100 Not valid email!</span>
32102 <tt>text = {{email.text}}</tt><br/>
32103 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
32104 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
32105 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
32106 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
32107 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
32110 <file name="protractor.js" type="protractor">
32111 var text = element(by.binding('email.text'));
32112 var valid = element(by.binding('myForm.input.$valid'));
32113 var input = element(by.model('email.text'));
32115 it('should initialize to model', function() {
32116 expect(text.getText()).toContain('me@example.com');
32117 expect(valid.getText()).toContain('true');
32120 it('should be invalid if empty', function() {
32122 input.sendKeys('');
32123 expect(text.getText()).toEqual('text =');
32124 expect(valid.getText()).toContain('false');
32127 it('should be invalid if not email', function() {
32129 input.sendKeys('xxx');
32131 expect(valid.getText()).toContain('false');
32136 'email': emailInputType,
32141 * @name input[radio]
32144 * HTML radio button.
32146 * @param {string} ngModel Assignable angular expression to data-bind to.
32147 * @param {string} value The value to which the `ngModel` expression should be set when selected.
32148 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
32149 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
32150 * @param {string=} name Property name of the form under which the control is published.
32151 * @param {string=} ngChange Angular expression to be executed when input changes due to user
32152 * interaction with the input element.
32153 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
32154 * is selected. Should be used instead of the `value` attribute if you need
32155 * a non-string `ngModel` (`boolean`, `array`, ...).
32158 <example name="radio-input-directive" module="radioExample">
32159 <file name="index.html">
32161 angular.module('radioExample', [])
32162 .controller('ExampleController', ['$scope', function($scope) {
32166 $scope.specialValue = {
32172 <form name="myForm" ng-controller="ExampleController">
32174 <input type="radio" ng-model="color.name" value="red">
32178 <input type="radio" ng-model="color.name" ng-value="specialValue">
32182 <input type="radio" ng-model="color.name" value="blue">
32185 <tt>color = {{color.name | json}}</tt><br/>
32187 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
32189 <file name="protractor.js" type="protractor">
32190 it('should change state', function() {
32191 var color = element(by.binding('color.name'));
32193 expect(color.getText()).toContain('blue');
32195 element.all(by.model('color.name')).get(0).click();
32197 expect(color.getText()).toContain('red');
32202 'radio': radioInputType,
32207 * @name input[checkbox]
32212 * @param {string} ngModel Assignable angular expression to data-bind to.
32213 * @param {string=} name Property name of the form under which the control is published.
32214 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
32215 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
32216 * @param {string=} ngChange Angular expression to be executed when input changes due to user
32217 * interaction with the input element.
32220 <example name="checkbox-input-directive" module="checkboxExample">
32221 <file name="index.html">
32223 angular.module('checkboxExample', [])
32224 .controller('ExampleController', ['$scope', function($scope) {
32225 $scope.checkboxModel = {
32231 <form name="myForm" ng-controller="ExampleController">
32233 <input type="checkbox" ng-model="checkboxModel.value1">
32236 <input type="checkbox" ng-model="checkboxModel.value2"
32237 ng-true-value="'YES'" ng-false-value="'NO'">
32239 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
32240 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
32243 <file name="protractor.js" type="protractor">
32244 it('should change state', function() {
32245 var value1 = element(by.binding('checkboxModel.value1'));
32246 var value2 = element(by.binding('checkboxModel.value2'));
32248 expect(value1.getText()).toContain('true');
32249 expect(value2.getText()).toContain('YES');
32251 element(by.model('checkboxModel.value1')).click();
32252 element(by.model('checkboxModel.value2')).click();
32254 expect(value1.getText()).toContain('false');
32255 expect(value2.getText()).toContain('NO');
32260 'checkbox': checkboxInputType,
32269 function stringBasedInputType(ctrl) {
32270 ctrl.$formatters.push(function(value) {
32271 return ctrl.$isEmpty(value) ? value : value.toString();
32275 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32276 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32277 stringBasedInputType(ctrl);
32280 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32281 var type = lowercase(element[0].type);
32283 // In composition mode, users are still inputing intermediate text buffer,
32284 // hold the listener until composition is done.
32285 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
32286 if (!$sniffer.android) {
32287 var composing = false;
32289 element.on('compositionstart', function() {
32293 element.on('compositionend', function() {
32301 var listener = function(ev) {
32303 $browser.defer.cancel(timeout);
32306 if (composing) return;
32307 var value = element.val(),
32308 event = ev && ev.type;
32310 // By default we will trim the value
32311 // If the attribute ng-trim exists we will avoid trimming
32312 // If input type is 'password', the value is never trimmed
32313 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
32314 value = trim(value);
32317 // If a control is suffering from bad input (due to native validators), browsers discard its
32318 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
32319 // control's value is the same empty value twice in a row.
32320 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
32321 ctrl.$setViewValue(value, event);
32325 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
32326 // input event on backspace, delete or cut
32327 if ($sniffer.hasEvent('input')) {
32328 element.on('input', listener);
32330 var deferListener = function(ev, input, origValue) {
32332 timeout = $browser.defer(function() {
32334 if (!input || input.value !== origValue) {
32341 element.on('keydown', function(event) {
32342 var key = event.keyCode;
32345 // command modifiers arrows
32346 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
32348 deferListener(event, this, this.value);
32351 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
32352 if ($sniffer.hasEvent('paste')) {
32353 element.on('paste cut', deferListener);
32357 // if user paste into input using mouse on older browser
32358 // or form autocomplete on newer browser, we need "change" event to catch it
32359 element.on('change', listener);
32361 // Some native input types (date-family) have the ability to change validity without
32362 // firing any input/change events.
32363 // For these event types, when native validators are present and the browser supports the type,
32364 // check for validity changes on various DOM events.
32365 if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
32366 element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
32368 var validity = this[VALIDITY_STATE_PROPERTY];
32369 var origBadInput = validity.badInput;
32370 var origTypeMismatch = validity.typeMismatch;
32371 timeout = $browser.defer(function() {
32373 if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
32381 ctrl.$render = function() {
32382 // Workaround for Firefox validation #12102.
32383 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
32384 if (element.val() !== value) {
32385 element.val(value);
32390 function weekParser(isoWeek, existingDate) {
32391 if (isDate(isoWeek)) {
32395 if (isString(isoWeek)) {
32396 WEEK_REGEXP.lastIndex = 0;
32397 var parts = WEEK_REGEXP.exec(isoWeek);
32399 var year = +parts[1],
32405 firstThurs = getFirstThursdayOfYear(year),
32406 addDays = (week - 1) * 7;
32408 if (existingDate) {
32409 hours = existingDate.getHours();
32410 minutes = existingDate.getMinutes();
32411 seconds = existingDate.getSeconds();
32412 milliseconds = existingDate.getMilliseconds();
32415 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
32422 function createDateParser(regexp, mapping) {
32423 return function(iso, date) {
32430 if (isString(iso)) {
32431 // When a date is JSON'ified to wraps itself inside of an extra
32432 // set of double quotes. This makes the date parsing code unable
32433 // to match the date string and parse it as a date.
32434 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
32435 iso = iso.substring(1, iso.length - 1);
32437 if (ISO_DATE_REGEXP.test(iso)) {
32438 return new Date(iso);
32440 regexp.lastIndex = 0;
32441 parts = regexp.exec(iso);
32447 yyyy: date.getFullYear(),
32448 MM: date.getMonth() + 1,
32449 dd: date.getDate(),
32450 HH: date.getHours(),
32451 mm: date.getMinutes(),
32452 ss: date.getSeconds(),
32453 sss: date.getMilliseconds() / 1000
32456 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
32459 forEach(parts, function(part, index) {
32460 if (index < mapping.length) {
32461 map[mapping[index]] = +part;
32464 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
32472 function createDateInputType(type, regexp, parseDate, format) {
32473 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
32474 badInputChecker(scope, element, attr, ctrl);
32475 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32476 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
32479 ctrl.$$parserName = type;
32480 ctrl.$parsers.push(function(value) {
32481 if (ctrl.$isEmpty(value)) return null;
32482 if (regexp.test(value)) {
32483 // Note: We cannot read ctrl.$modelValue, as there might be a different
32484 // parser/formatter in the processing chain so that the model
32485 // contains some different data format!
32486 var parsedDate = parseDate(value, previousDate);
32488 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
32495 ctrl.$formatters.push(function(value) {
32496 if (value && !isDate(value)) {
32497 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
32499 if (isValidDate(value)) {
32500 previousDate = value;
32501 if (previousDate && timezone) {
32502 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
32504 return $filter('date')(value, format, timezone);
32506 previousDate = null;
32511 if (isDefined(attr.min) || attr.ngMin) {
32513 ctrl.$validators.min = function(value) {
32514 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
32516 attr.$observe('min', function(val) {
32517 minVal = parseObservedDateValue(val);
32522 if (isDefined(attr.max) || attr.ngMax) {
32524 ctrl.$validators.max = function(value) {
32525 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
32527 attr.$observe('max', function(val) {
32528 maxVal = parseObservedDateValue(val);
32533 function isValidDate(value) {
32534 // Invalid Date: getTime() returns NaN
32535 return value && !(value.getTime && value.getTime() !== value.getTime());
32538 function parseObservedDateValue(val) {
32539 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
32544 function badInputChecker(scope, element, attr, ctrl) {
32545 var node = element[0];
32546 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
32547 if (nativeValidation) {
32548 ctrl.$parsers.push(function(value) {
32549 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
32550 return validity.badInput || validity.typeMismatch ? undefined : value;
32555 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32556 badInputChecker(scope, element, attr, ctrl);
32557 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32559 ctrl.$$parserName = 'number';
32560 ctrl.$parsers.push(function(value) {
32561 if (ctrl.$isEmpty(value)) return null;
32562 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
32566 ctrl.$formatters.push(function(value) {
32567 if (!ctrl.$isEmpty(value)) {
32568 if (!isNumber(value)) {
32569 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
32571 value = value.toString();
32576 if (isDefined(attr.min) || attr.ngMin) {
32578 ctrl.$validators.min = function(value) {
32579 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
32582 attr.$observe('min', function(val) {
32583 if (isDefined(val) && !isNumber(val)) {
32584 val = parseFloat(val, 10);
32586 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
32587 // TODO(matsko): implement validateLater to reduce number of validations
32592 if (isDefined(attr.max) || attr.ngMax) {
32594 ctrl.$validators.max = function(value) {
32595 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
32598 attr.$observe('max', function(val) {
32599 if (isDefined(val) && !isNumber(val)) {
32600 val = parseFloat(val, 10);
32602 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
32603 // TODO(matsko): implement validateLater to reduce number of validations
32609 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32610 // Note: no badInputChecker here by purpose as `url` is only a validation
32611 // in browsers, i.e. we can always read out input.value even if it is not valid!
32612 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32613 stringBasedInputType(ctrl);
32615 ctrl.$$parserName = 'url';
32616 ctrl.$validators.url = function(modelValue, viewValue) {
32617 var value = modelValue || viewValue;
32618 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
32622 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32623 // Note: no badInputChecker here by purpose as `url` is only a validation
32624 // in browsers, i.e. we can always read out input.value even if it is not valid!
32625 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32626 stringBasedInputType(ctrl);
32628 ctrl.$$parserName = 'email';
32629 ctrl.$validators.email = function(modelValue, viewValue) {
32630 var value = modelValue || viewValue;
32631 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
32635 function radioInputType(scope, element, attr, ctrl) {
32636 // make the name unique, if not defined
32637 if (isUndefined(attr.name)) {
32638 element.attr('name', nextUid());
32641 var listener = function(ev) {
32642 if (element[0].checked) {
32643 ctrl.$setViewValue(attr.value, ev && ev.type);
32647 element.on('click', listener);
32649 ctrl.$render = function() {
32650 var value = attr.value;
32651 element[0].checked = (value == ctrl.$viewValue);
32654 attr.$observe('value', ctrl.$render);
32657 function parseConstantExpr($parse, context, name, expression, fallback) {
32659 if (isDefined(expression)) {
32660 parseFn = $parse(expression);
32661 if (!parseFn.constant) {
32662 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
32663 '`{1}`.', name, expression);
32665 return parseFn(context);
32670 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
32671 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
32672 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
32674 var listener = function(ev) {
32675 ctrl.$setViewValue(element[0].checked, ev && ev.type);
32678 element.on('click', listener);
32680 ctrl.$render = function() {
32681 element[0].checked = ctrl.$viewValue;
32684 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
32685 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
32686 // it to a boolean.
32687 ctrl.$isEmpty = function(value) {
32688 return value === false;
32691 ctrl.$formatters.push(function(value) {
32692 return equals(value, trueValue);
32695 ctrl.$parsers.push(function(value) {
32696 return value ? trueValue : falseValue;
32707 * HTML textarea element control with angular data-binding. The data-binding and validation
32708 * properties of this element are exactly the same as those of the
32709 * {@link ng.directive:input input element}.
32711 * @param {string} ngModel Assignable angular expression to data-bind to.
32712 * @param {string=} name Property name of the form under which the control is published.
32713 * @param {string=} required Sets `required` validation error key if the value is not entered.
32714 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
32715 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
32716 * `required` when you want to data-bind to the `required` attribute.
32717 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
32719 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
32720 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
32722 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
32723 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
32724 * If the expression evaluates to a RegExp object, then this is used directly.
32725 * If the expression evaluates to a string, then it will be converted to a RegExp
32726 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
32727 * `new RegExp('^abc$')`.<br />
32728 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32729 * start at the index of the last search's match, thus not taking the whole input value into
32731 * @param {string=} ngChange Angular expression to be executed when input changes due to user
32732 * interaction with the input element.
32733 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
32743 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
32744 * input state control, and validation.
32745 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
32747 * <div class="alert alert-warning">
32748 * **Note:** Not every feature offered is available for all input types.
32749 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
32752 * @param {string} ngModel Assignable angular expression to data-bind to.
32753 * @param {string=} name Property name of the form under which the control is published.
32754 * @param {string=} required Sets `required` validation error key if the value is not entered.
32755 * @param {boolean=} ngRequired Sets `required` attribute if set to true
32756 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
32758 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
32759 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
32761 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
32762 * value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
32763 * If the expression evaluates to a RegExp object, then this is used directly.
32764 * If the expression evaluates to a string, then it will be converted to a RegExp
32765 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
32766 * `new RegExp('^abc$')`.<br />
32767 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32768 * start at the index of the last search's match, thus not taking the whole input value into
32770 * @param {string=} ngChange Angular expression to be executed when input changes due to user
32771 * interaction with the input element.
32772 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
32773 * This parameter is ignored for input[type=password] controls, which will never trim the
32777 <example name="input-directive" module="inputExample">
32778 <file name="index.html">
32780 angular.module('inputExample', [])
32781 .controller('ExampleController', ['$scope', function($scope) {
32782 $scope.user = {name: 'guest', last: 'visitor'};
32785 <div ng-controller="ExampleController">
32786 <form name="myForm">
32789 <input type="text" name="userName" ng-model="user.name" required>
32792 <span class="error" ng-show="myForm.userName.$error.required">
32797 <input type="text" name="lastName" ng-model="user.last"
32798 ng-minlength="3" ng-maxlength="10">
32801 <span class="error" ng-show="myForm.lastName.$error.minlength">
32803 <span class="error" ng-show="myForm.lastName.$error.maxlength">
32808 <tt>user = {{user}}</tt><br/>
32809 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
32810 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
32811 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
32812 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
32813 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
32814 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
32815 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
32816 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
32819 <file name="protractor.js" type="protractor">
32820 var user = element(by.exactBinding('user'));
32821 var userNameValid = element(by.binding('myForm.userName.$valid'));
32822 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
32823 var lastNameError = element(by.binding('myForm.lastName.$error'));
32824 var formValid = element(by.binding('myForm.$valid'));
32825 var userNameInput = element(by.model('user.name'));
32826 var userLastInput = element(by.model('user.last'));
32828 it('should initialize to model', function() {
32829 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
32830 expect(userNameValid.getText()).toContain('true');
32831 expect(formValid.getText()).toContain('true');
32834 it('should be invalid if empty when required', function() {
32835 userNameInput.clear();
32836 userNameInput.sendKeys('');
32838 expect(user.getText()).toContain('{"last":"visitor"}');
32839 expect(userNameValid.getText()).toContain('false');
32840 expect(formValid.getText()).toContain('false');
32843 it('should be valid if empty when min length is set', function() {
32844 userLastInput.clear();
32845 userLastInput.sendKeys('');
32847 expect(user.getText()).toContain('{"name":"guest","last":""}');
32848 expect(lastNameValid.getText()).toContain('true');
32849 expect(formValid.getText()).toContain('true');
32852 it('should be invalid if less than required min length', function() {
32853 userLastInput.clear();
32854 userLastInput.sendKeys('xx');
32856 expect(user.getText()).toContain('{"name":"guest"}');
32857 expect(lastNameValid.getText()).toContain('false');
32858 expect(lastNameError.getText()).toContain('minlength');
32859 expect(formValid.getText()).toContain('false');
32862 it('should be invalid if longer than max length', function() {
32863 userLastInput.clear();
32864 userLastInput.sendKeys('some ridiculously long name');
32866 expect(user.getText()).toContain('{"name":"guest"}');
32867 expect(lastNameValid.getText()).toContain('false');
32868 expect(lastNameError.getText()).toContain('maxlength');
32869 expect(formValid.getText()).toContain('false');
32874 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
32875 function($browser, $sniffer, $filter, $parse) {
32878 require: ['?ngModel'],
32880 pre: function(scope, element, attr, ctrls) {
32882 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
32883 $browser, $filter, $parse);
32892 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
32898 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
32899 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
32902 * `ngValue` is useful when dynamically generating lists of radio buttons using
32903 * {@link ngRepeat `ngRepeat`}, as shown below.
32905 * Likewise, `ngValue` can be used to generate `<option>` elements for
32906 * the {@link select `select`} element. In that case however, only strings are supported
32907 * for the `value `attribute, so the resulting `ngModel` will always be a string.
32908 * Support for `select` models with non-string values is available via `ngOptions`.
32911 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
32912 * of the `input` element
32915 <example name="ngValue-directive" module="valueExample">
32916 <file name="index.html">
32918 angular.module('valueExample', [])
32919 .controller('ExampleController', ['$scope', function($scope) {
32920 $scope.names = ['pizza', 'unicorns', 'robots'];
32921 $scope.my = { favorite: 'unicorns' };
32924 <form ng-controller="ExampleController">
32925 <h2>Which is your favorite?</h2>
32926 <label ng-repeat="name in names" for="{{name}}">
32928 <input type="radio"
32929 ng-model="my.favorite"
32934 <div>You chose {{my.favorite}}</div>
32937 <file name="protractor.js" type="protractor">
32938 var favorite = element(by.binding('my.favorite'));
32940 it('should initialize to model', function() {
32941 expect(favorite.getText()).toContain('unicorns');
32943 it('should bind the values to the inputs', function() {
32944 element.all(by.model('my.favorite')).get(0).click();
32945 expect(favorite.getText()).toContain('pizza');
32950 var ngValueDirective = function() {
32954 compile: function(tpl, tplAttr) {
32955 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
32956 return function ngValueConstantLink(scope, elm, attr) {
32957 attr.$set('value', scope.$eval(attr.ngValue));
32960 return function ngValueLink(scope, elm, attr) {
32961 scope.$watch(attr.ngValue, function valueWatchAction(value) {
32962 attr.$set('value', value);
32976 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
32977 * with the value of a given expression, and to update the text content when the value of that
32978 * expression changes.
32980 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
32981 * `{{ expression }}` which is similar but less verbose.
32983 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
32984 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
32985 * element attribute, it makes the bindings invisible to the user while the page is loading.
32987 * An alternative solution to this problem would be using the
32988 * {@link ng.directive:ngCloak ngCloak} directive.
32992 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
32995 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
32996 <example module="bindExample">
32997 <file name="index.html">
32999 angular.module('bindExample', [])
33000 .controller('ExampleController', ['$scope', function($scope) {
33001 $scope.name = 'Whirled';
33004 <div ng-controller="ExampleController">
33005 <label>Enter name: <input type="text" ng-model="name"></label><br>
33006 Hello <span ng-bind="name"></span>!
33009 <file name="protractor.js" type="protractor">
33010 it('should check ng-bind', function() {
33011 var nameInput = element(by.model('name'));
33013 expect(element(by.binding('name')).getText()).toBe('Whirled');
33015 nameInput.sendKeys('world');
33016 expect(element(by.binding('name')).getText()).toBe('world');
33021 var ngBindDirective = ['$compile', function($compile) {
33024 compile: function ngBindCompile(templateElement) {
33025 $compile.$$addBindingClass(templateElement);
33026 return function ngBindLink(scope, element, attr) {
33027 $compile.$$addBindingInfo(element, attr.ngBind);
33028 element = element[0];
33029 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
33030 element.textContent = isUndefined(value) ? '' : value;
33040 * @name ngBindTemplate
33043 * The `ngBindTemplate` directive specifies that the element
33044 * text content should be replaced with the interpolation of the template
33045 * in the `ngBindTemplate` attribute.
33046 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
33047 * expressions. This directive is needed since some HTML elements
33048 * (such as TITLE and OPTION) cannot contain SPAN elements.
33051 * @param {string} ngBindTemplate template of form
33052 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
33055 * Try it here: enter text in text box and watch the greeting change.
33056 <example module="bindExample">
33057 <file name="index.html">
33059 angular.module('bindExample', [])
33060 .controller('ExampleController', ['$scope', function($scope) {
33061 $scope.salutation = 'Hello';
33062 $scope.name = 'World';
33065 <div ng-controller="ExampleController">
33066 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
33067 <label>Name: <input type="text" ng-model="name"></label><br>
33068 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
33071 <file name="protractor.js" type="protractor">
33072 it('should check ng-bind', function() {
33073 var salutationElem = element(by.binding('salutation'));
33074 var salutationInput = element(by.model('salutation'));
33075 var nameInput = element(by.model('name'));
33077 expect(salutationElem.getText()).toBe('Hello World!');
33079 salutationInput.clear();
33080 salutationInput.sendKeys('Greetings');
33082 nameInput.sendKeys('user');
33084 expect(salutationElem.getText()).toBe('Greetings user!');
33089 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
33091 compile: function ngBindTemplateCompile(templateElement) {
33092 $compile.$$addBindingClass(templateElement);
33093 return function ngBindTemplateLink(scope, element, attr) {
33094 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
33095 $compile.$$addBindingInfo(element, interpolateFn.expressions);
33096 element = element[0];
33097 attr.$observe('ngBindTemplate', function(value) {
33098 element.textContent = isUndefined(value) ? '' : value;
33111 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
33112 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
33113 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
33114 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
33115 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
33117 * You may also bypass sanitization for values you know are safe. To do so, bind to
33118 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
33119 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
33121 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
33122 * will have an exception (instead of an exploit.)
33125 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
33129 <example module="bindHtmlExample" deps="angular-sanitize.js">
33130 <file name="index.html">
33131 <div ng-controller="ExampleController">
33132 <p ng-bind-html="myHTML"></p>
33136 <file name="script.js">
33137 angular.module('bindHtmlExample', ['ngSanitize'])
33138 .controller('ExampleController', ['$scope', function($scope) {
33140 'I am an <code>HTML</code>string with ' +
33141 '<a href="#">links!</a> and other <em>stuff</em>';
33145 <file name="protractor.js" type="protractor">
33146 it('should check ng-bind-html', function() {
33147 expect(element(by.binding('myHTML')).getText()).toBe(
33148 'I am an HTMLstring with links! and other stuff');
33153 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
33156 compile: function ngBindHtmlCompile(tElement, tAttrs) {
33157 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
33158 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
33159 return (value || '').toString();
33161 $compile.$$addBindingClass(tElement);
33163 return function ngBindHtmlLink(scope, element, attr) {
33164 $compile.$$addBindingInfo(element, attr.ngBindHtml);
33166 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
33167 // we re-evaluate the expr because we want a TrustedValueHolderType
33168 // for $sce, not a string
33169 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
33181 * Evaluate the given expression when the user changes the input.
33182 * The expression is evaluated immediately, unlike the JavaScript onchange event
33183 * which only triggers at the end of a change (usually, when the user leaves the
33184 * form element or presses the return key).
33186 * The `ngChange` expression is only evaluated when a change in the input value causes
33187 * a new value to be committed to the model.
33189 * It will not be evaluated:
33190 * * if the value returned from the `$parsers` transformation pipeline has not changed
33191 * * if the input has continued to be invalid since the model will stay `null`
33192 * * if the model is changed programmatically and not by a change to the input value
33195 * Note, this directive requires `ngModel` to be present.
33198 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
33202 * <example name="ngChange-directive" module="changeExample">
33203 * <file name="index.html">
33205 * angular.module('changeExample', [])
33206 * .controller('ExampleController', ['$scope', function($scope) {
33207 * $scope.counter = 0;
33208 * $scope.change = function() {
33209 * $scope.counter++;
33213 * <div ng-controller="ExampleController">
33214 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
33215 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
33216 * <label for="ng-change-example2">Confirmed</label><br />
33217 * <tt>debug = {{confirmed}}</tt><br/>
33218 * <tt>counter = {{counter}}</tt><br/>
33221 * <file name="protractor.js" type="protractor">
33222 * var counter = element(by.binding('counter'));
33223 * var debug = element(by.binding('confirmed'));
33225 * it('should evaluate the expression if changing from view', function() {
33226 * expect(counter.getText()).toContain('0');
33228 * element(by.id('ng-change-example1')).click();
33230 * expect(counter.getText()).toContain('1');
33231 * expect(debug.getText()).toContain('true');
33234 * it('should not evaluate the expression if changing from model', function() {
33235 * element(by.id('ng-change-example2')).click();
33237 * expect(counter.getText()).toContain('0');
33238 * expect(debug.getText()).toContain('true');
33243 var ngChangeDirective = valueFn({
33245 require: 'ngModel',
33246 link: function(scope, element, attr, ctrl) {
33247 ctrl.$viewChangeListeners.push(function() {
33248 scope.$eval(attr.ngChange);
33253 function classDirective(name, selector) {
33254 name = 'ngClass' + name;
33255 return ['$animate', function($animate) {
33258 link: function(scope, element, attr) {
33261 scope.$watch(attr[name], ngClassWatchAction, true);
33263 attr.$observe('class', function(value) {
33264 ngClassWatchAction(scope.$eval(attr[name]));
33268 if (name !== 'ngClass') {
33269 scope.$watch('$index', function($index, old$index) {
33270 // jshint bitwise: false
33271 var mod = $index & 1;
33272 if (mod !== (old$index & 1)) {
33273 var classes = arrayClasses(scope.$eval(attr[name]));
33275 addClasses(classes) :
33276 removeClasses(classes);
33281 function addClasses(classes) {
33282 var newClasses = digestClassCounts(classes, 1);
33283 attr.$addClass(newClasses);
33286 function removeClasses(classes) {
33287 var newClasses = digestClassCounts(classes, -1);
33288 attr.$removeClass(newClasses);
33291 function digestClassCounts(classes, count) {
33292 // Use createMap() to prevent class assumptions involving property
33293 // names in Object.prototype
33294 var classCounts = element.data('$classCounts') || createMap();
33295 var classesToUpdate = [];
33296 forEach(classes, function(className) {
33297 if (count > 0 || classCounts[className]) {
33298 classCounts[className] = (classCounts[className] || 0) + count;
33299 if (classCounts[className] === +(count > 0)) {
33300 classesToUpdate.push(className);
33304 element.data('$classCounts', classCounts);
33305 return classesToUpdate.join(' ');
33308 function updateClasses(oldClasses, newClasses) {
33309 var toAdd = arrayDifference(newClasses, oldClasses);
33310 var toRemove = arrayDifference(oldClasses, newClasses);
33311 toAdd = digestClassCounts(toAdd, 1);
33312 toRemove = digestClassCounts(toRemove, -1);
33313 if (toAdd && toAdd.length) {
33314 $animate.addClass(element, toAdd);
33316 if (toRemove && toRemove.length) {
33317 $animate.removeClass(element, toRemove);
33321 function ngClassWatchAction(newVal) {
33322 if (selector === true || scope.$index % 2 === selector) {
33323 var newClasses = arrayClasses(newVal || []);
33325 addClasses(newClasses);
33326 } else if (!equals(newVal,oldVal)) {
33327 var oldClasses = arrayClasses(oldVal);
33328 updateClasses(oldClasses, newClasses);
33331 if (isArray(newVal)) {
33332 oldVal = newVal.map(function(v) { return shallowCopy(v); });
33334 oldVal = shallowCopy(newVal);
33340 function arrayDifference(tokens1, tokens2) {
33344 for (var i = 0; i < tokens1.length; i++) {
33345 var token = tokens1[i];
33346 for (var j = 0; j < tokens2.length; j++) {
33347 if (token == tokens2[j]) continue outer;
33349 values.push(token);
33354 function arrayClasses(classVal) {
33356 if (isArray(classVal)) {
33357 forEach(classVal, function(v) {
33358 classes = classes.concat(arrayClasses(v));
33361 } else if (isString(classVal)) {
33362 return classVal.split(' ');
33363 } else if (isObject(classVal)) {
33364 forEach(classVal, function(v, k) {
33366 classes = classes.concat(k.split(' '));
33382 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
33383 * an expression that represents all classes to be added.
33385 * The directive operates in three different ways, depending on which of three types the expression
33388 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
33391 * 2. If the expression evaluates to an object, then for each key-value pair of the
33392 * object with a truthy value the corresponding key is used as a class name.
33394 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
33395 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
33396 * to give you more control over what CSS classes appear. See the code below for an example of this.
33399 * The directive won't add duplicate classes if a particular class was already set.
33401 * When the expression changes, the previously added classes are removed and only then are the
33402 * new classes added.
33405 * | Animation | Occurs |
33406 * |----------------------------------|-------------------------------------|
33407 * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
33408 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
33411 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
33412 * of the evaluation can be a string representing space delimited class
33413 * names, an array, or a map of class names to boolean values. In the case of a map, the
33414 * names of the properties whose values are truthy will be added as css classes to the
33417 * @example Example that demonstrates basic bindings via ngClass directive.
33419 <file name="index.html">
33420 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
33422 <input type="checkbox" ng-model="deleted">
33423 deleted (apply "strike" class)
33426 <input type="checkbox" ng-model="important">
33427 important (apply "bold" class)
33430 <input type="checkbox" ng-model="error">
33431 error (apply "has-error" class)
33434 <p ng-class="style">Using String Syntax</p>
33435 <input type="text" ng-model="style"
33436 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
33438 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
33439 <input ng-model="style1"
33440 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
33441 <input ng-model="style2"
33442 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
33443 <input ng-model="style3"
33444 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
33446 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
33447 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
33448 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
33450 <file name="style.css">
33452 text-decoration: line-through;
33462 background-color: yellow;
33468 <file name="protractor.js" type="protractor">
33469 var ps = element.all(by.css('p'));
33471 it('should let you toggle the class', function() {
33473 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
33474 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
33476 element(by.model('important')).click();
33477 expect(ps.first().getAttribute('class')).toMatch(/bold/);
33479 element(by.model('error')).click();
33480 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
33483 it('should let you toggle string example', function() {
33484 expect(ps.get(1).getAttribute('class')).toBe('');
33485 element(by.model('style')).clear();
33486 element(by.model('style')).sendKeys('red');
33487 expect(ps.get(1).getAttribute('class')).toBe('red');
33490 it('array example should have 3 classes', function() {
33491 expect(ps.get(2).getAttribute('class')).toBe('');
33492 element(by.model('style1')).sendKeys('bold');
33493 element(by.model('style2')).sendKeys('strike');
33494 element(by.model('style3')).sendKeys('red');
33495 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
33498 it('array with map example should have 2 classes', function() {
33499 expect(ps.last().getAttribute('class')).toBe('');
33500 element(by.model('style4')).sendKeys('bold');
33501 element(by.model('warning')).click();
33502 expect(ps.last().getAttribute('class')).toBe('bold orange');
33509 The example below demonstrates how to perform animations using ngClass.
33511 <example module="ngAnimate" deps="angular-animate.js" animations="true">
33512 <file name="index.html">
33513 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
33514 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
33516 <span class="base-class" ng-class="myVar">Sample Text</span>
33518 <file name="style.css">
33520 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
33523 .base-class.my-class {
33528 <file name="protractor.js" type="protractor">
33529 it('should check ng-class', function() {
33530 expect(element(by.css('.base-class')).getAttribute('class')).not.
33531 toMatch(/my-class/);
33533 element(by.id('setbtn')).click();
33535 expect(element(by.css('.base-class')).getAttribute('class')).
33536 toMatch(/my-class/);
33538 element(by.id('clearbtn')).click();
33540 expect(element(by.css('.base-class')).getAttribute('class')).not.
33541 toMatch(/my-class/);
33547 ## ngClass and pre-existing CSS3 Transitions/Animations
33548 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
33549 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
33550 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
33551 to view the step by step details of {@link $animate#addClass $animate.addClass} and
33552 {@link $animate#removeClass $animate.removeClass}.
33554 var ngClassDirective = classDirective('', true);
33562 * The `ngClassOdd` and `ngClassEven` directives work exactly as
33563 * {@link ng.directive:ngClass ngClass}, except they work in
33564 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
33566 * This directive can be applied only within the scope of an
33567 * {@link ng.directive:ngRepeat ngRepeat}.
33570 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
33571 * of the evaluation can be a string representing space delimited class names or an array.
33575 <file name="index.html">
33576 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
33577 <li ng-repeat="name in names">
33578 <span ng-class-odd="'odd'" ng-class-even="'even'">
33584 <file name="style.css">
33592 <file name="protractor.js" type="protractor">
33593 it('should check ng-class-odd and ng-class-even', function() {
33594 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
33596 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
33602 var ngClassOddDirective = classDirective('Odd', 0);
33606 * @name ngClassEven
33610 * The `ngClassOdd` and `ngClassEven` directives work exactly as
33611 * {@link ng.directive:ngClass ngClass}, except they work in
33612 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
33614 * This directive can be applied only within the scope of an
33615 * {@link ng.directive:ngRepeat ngRepeat}.
33618 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
33619 * result of the evaluation can be a string representing space delimited class names or an array.
33623 <file name="index.html">
33624 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
33625 <li ng-repeat="name in names">
33626 <span ng-class-odd="'odd'" ng-class-even="'even'">
33627 {{name}}
33632 <file name="style.css">
33640 <file name="protractor.js" type="protractor">
33641 it('should check ng-class-odd and ng-class-even', function() {
33642 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
33644 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
33650 var ngClassEvenDirective = classDirective('Even', 1);
33658 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
33659 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
33660 * directive to avoid the undesirable flicker effect caused by the html template display.
33662 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
33663 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
33664 * of the browser view.
33666 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
33667 * `angular.min.js`.
33668 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
33671 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
33672 * display: none !important;
33676 * When this css rule is loaded by the browser, all html elements (including their children) that
33677 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
33678 * during the compilation of the template it deletes the `ngCloak` element attribute, making
33679 * the compiled element visible.
33681 * For the best result, the `angular.js` script must be loaded in the head section of the html
33682 * document; alternatively, the css rule above must be included in the external stylesheet of the
33689 <file name="index.html">
33690 <div id="template1" ng-cloak>{{ 'hello' }}</div>
33691 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
33693 <file name="protractor.js" type="protractor">
33694 it('should remove the template directive and css class', function() {
33695 expect($('#template1').getAttribute('ng-cloak')).
33697 expect($('#template2').getAttribute('ng-cloak')).
33704 var ngCloakDirective = ngDirective({
33705 compile: function(element, attr) {
33706 attr.$set('ngCloak', undefined);
33707 element.removeClass('ng-cloak');
33713 * @name ngController
33716 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
33717 * supports the principles behind the Model-View-Controller design pattern.
33719 * MVC components in angular:
33721 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
33722 * are accessed through bindings.
33723 * * View — The template (HTML with data bindings) that is rendered into the View.
33724 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
33725 * logic behind the application to decorate the scope with functions and values
33727 * Note that you can also attach controllers to the DOM by declaring it in a route definition
33728 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
33729 * again using `ng-controller` in the template itself. This will cause the controller to be attached
33730 * and executed twice.
33735 * @param {expression} ngController Name of a constructor function registered with the current
33736 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
33737 * that on the current scope evaluates to a constructor function.
33739 * The controller instance can be published into a scope property by specifying
33740 * `ng-controller="as propertyName"`.
33742 * If the current `$controllerProvider` is configured to use globals (via
33743 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
33744 * also be the name of a globally accessible constructor function (not recommended).
33747 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
33748 * greeting are methods declared on the controller (see source tab). These methods can
33749 * easily be called from the angular markup. Any changes to the data are automatically reflected
33750 * in the View without the need for a manual update.
33752 * Two different declaration styles are included below:
33754 * * one binds methods and properties directly onto the controller using `this`:
33755 * `ng-controller="SettingsController1 as settings"`
33756 * * one injects `$scope` into the controller:
33757 * `ng-controller="SettingsController2"`
33759 * The second option is more common in the Angular community, and is generally used in boilerplates
33760 * and in this guide. However, there are advantages to binding properties directly to the controller
33761 * and avoiding scope.
33763 * * Using `controller as` makes it obvious which controller you are accessing in the template when
33764 * multiple controllers apply to an element.
33765 * * If you are writing your controllers as classes you have easier access to the properties and
33766 * methods, which will appear on the scope, from inside the controller code.
33767 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
33768 * inheritance masking primitives.
33770 * This example demonstrates the `controller as` syntax.
33772 * <example name="ngControllerAs" module="controllerAsExample">
33773 * <file name="index.html">
33774 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
33775 * <label>Name: <input type="text" ng-model="settings.name"/></label>
33776 * <button ng-click="settings.greet()">greet</button><br/>
33779 * <li ng-repeat="contact in settings.contacts">
33780 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
33781 * <option>phone</option>
33782 * <option>email</option>
33784 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
33785 * <button ng-click="settings.clearContact(contact)">clear</button>
33786 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
33788 * <li><button ng-click="settings.addContact()">add</button></li>
33792 * <file name="app.js">
33793 * angular.module('controllerAsExample', [])
33794 * .controller('SettingsController1', SettingsController1);
33796 * function SettingsController1() {
33797 * this.name = "John Smith";
33798 * this.contacts = [
33799 * {type: 'phone', value: '408 555 1212'},
33800 * {type: 'email', value: 'john.smith@example.org'} ];
33803 * SettingsController1.prototype.greet = function() {
33804 * alert(this.name);
33807 * SettingsController1.prototype.addContact = function() {
33808 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
33811 * SettingsController1.prototype.removeContact = function(contactToRemove) {
33812 * var index = this.contacts.indexOf(contactToRemove);
33813 * this.contacts.splice(index, 1);
33816 * SettingsController1.prototype.clearContact = function(contact) {
33817 * contact.type = 'phone';
33818 * contact.value = '';
33821 * <file name="protractor.js" type="protractor">
33822 * it('should check controller as', function() {
33823 * var container = element(by.id('ctrl-as-exmpl'));
33824 * expect(container.element(by.model('settings.name'))
33825 * .getAttribute('value')).toBe('John Smith');
33827 * var firstRepeat =
33828 * container.element(by.repeater('contact in settings.contacts').row(0));
33829 * var secondRepeat =
33830 * container.element(by.repeater('contact in settings.contacts').row(1));
33832 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33833 * .toBe('408 555 1212');
33835 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
33836 * .toBe('john.smith@example.org');
33838 * firstRepeat.element(by.buttonText('clear')).click();
33840 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33843 * container.element(by.buttonText('add')).click();
33845 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
33846 * .element(by.model('contact.value'))
33847 * .getAttribute('value'))
33848 * .toBe('yourname@example.org');
33853 * This example demonstrates the "attach to `$scope`" style of controller.
33855 * <example name="ngController" module="controllerExample">
33856 * <file name="index.html">
33857 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
33858 * <label>Name: <input type="text" ng-model="name"/></label>
33859 * <button ng-click="greet()">greet</button><br/>
33862 * <li ng-repeat="contact in contacts">
33863 * <select ng-model="contact.type" id="select_{{$index}}">
33864 * <option>phone</option>
33865 * <option>email</option>
33867 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
33868 * <button ng-click="clearContact(contact)">clear</button>
33869 * <button ng-click="removeContact(contact)">X</button>
33871 * <li>[ <button ng-click="addContact()">add</button> ]</li>
33875 * <file name="app.js">
33876 * angular.module('controllerExample', [])
33877 * .controller('SettingsController2', ['$scope', SettingsController2]);
33879 * function SettingsController2($scope) {
33880 * $scope.name = "John Smith";
33881 * $scope.contacts = [
33882 * {type:'phone', value:'408 555 1212'},
33883 * {type:'email', value:'john.smith@example.org'} ];
33885 * $scope.greet = function() {
33886 * alert($scope.name);
33889 * $scope.addContact = function() {
33890 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
33893 * $scope.removeContact = function(contactToRemove) {
33894 * var index = $scope.contacts.indexOf(contactToRemove);
33895 * $scope.contacts.splice(index, 1);
33898 * $scope.clearContact = function(contact) {
33899 * contact.type = 'phone';
33900 * contact.value = '';
33904 * <file name="protractor.js" type="protractor">
33905 * it('should check controller', function() {
33906 * var container = element(by.id('ctrl-exmpl'));
33908 * expect(container.element(by.model('name'))
33909 * .getAttribute('value')).toBe('John Smith');
33911 * var firstRepeat =
33912 * container.element(by.repeater('contact in contacts').row(0));
33913 * var secondRepeat =
33914 * container.element(by.repeater('contact in contacts').row(1));
33916 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33917 * .toBe('408 555 1212');
33918 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
33919 * .toBe('john.smith@example.org');
33921 * firstRepeat.element(by.buttonText('clear')).click();
33923 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33926 * container.element(by.buttonText('add')).click();
33928 * expect(container.element(by.repeater('contact in contacts').row(2))
33929 * .element(by.model('contact.value'))
33930 * .getAttribute('value'))
33931 * .toBe('yourname@example.org');
33937 var ngControllerDirective = [function() {
33953 * Angular has some features that can break certain
33954 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
33956 * If you intend to implement these rules then you must tell Angular not to use these features.
33958 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
33961 * The following rules affect Angular:
33963 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
33964 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
33965 * increase in the speed of evaluating Angular expressions.
33967 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
33968 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
33969 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
33970 * `angular-csp.css` in your HTML manually.
33972 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
33973 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
33974 * however, triggers a CSP error to be logged in the console:
33977 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
33978 * script in the following Content Security Policy directive: "default-src 'self'". Note that
33979 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
33982 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
33983 * directive on an element of the HTML document that appears before the `<script>` tag that loads
33984 * the `angular.js` file.
33986 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
33988 * You can specify which of the CSP related Angular features should be deactivated by providing
33989 * a value for the `ng-csp` attribute. The options are as follows:
33991 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
33993 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
33995 * You can use these values in the following combinations:
33998 * * No declaration means that Angular will assume that you can do inline styles, but it will do
33999 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
34002 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
34003 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
34006 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
34007 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
34009 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
34010 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
34012 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
34013 * styles nor use eval, which is the same as an empty: ng-csp.
34014 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
34017 * This example shows how to apply the `ngCsp` directive to the `html` tag.
34020 <html ng-app ng-csp>
34026 // Note: the suffix `.csp` in the example name triggers
34027 // csp mode in our http server!
34028 <example name="example.csp" module="cspExample" ng-csp="true">
34029 <file name="index.html">
34030 <div ng-controller="MainController as ctrl">
34032 <button ng-click="ctrl.inc()" id="inc">Increment</button>
34033 <span id="counter">
34039 <button ng-click="ctrl.evil()" id="evil">Evil</button>
34040 <span id="evilError">
34046 <file name="script.js">
34047 angular.module('cspExample', [])
34048 .controller('MainController', function() {
34050 this.inc = function() {
34053 this.evil = function() {
34054 // jshint evil:true
34058 this.evilError = e.message;
34063 <file name="protractor.js" type="protractor">
34064 var util, webdriver;
34066 var incBtn = element(by.id('inc'));
34067 var counter = element(by.id('counter'));
34068 var evilBtn = element(by.id('evil'));
34069 var evilError = element(by.id('evilError'));
34071 function getAndClearSevereErrors() {
34072 return browser.manage().logs().get('browser').then(function(browserLog) {
34073 return browserLog.filter(function(logEntry) {
34074 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
34079 function clearErrors() {
34080 getAndClearSevereErrors();
34083 function expectNoErrors() {
34084 getAndClearSevereErrors().then(function(filteredLog) {
34085 expect(filteredLog.length).toEqual(0);
34086 if (filteredLog.length) {
34087 console.log('browser console errors: ' + util.inspect(filteredLog));
34092 function expectError(regex) {
34093 getAndClearSevereErrors().then(function(filteredLog) {
34095 filteredLog.forEach(function(log) {
34096 if (log.message.match(regex)) {
34101 throw new Error('expected an error that matches ' + regex);
34106 beforeEach(function() {
34107 util = require('util');
34108 webdriver = require('protractor/node_modules/selenium-webdriver');
34111 // For now, we only test on Chrome,
34112 // as Safari does not load the page with Protractor's injected scripts,
34113 // and Firefox webdriver always disables content security policy (#6358)
34114 if (browser.params.browser !== 'chrome') {
34118 it('should not report errors when the page is loaded', function() {
34119 // clear errors so we are not dependent on previous tests
34121 // Need to reload the page as the page is already loaded when
34123 browser.driver.getCurrentUrl().then(function(url) {
34129 it('should evaluate expressions', function() {
34130 expect(counter.getText()).toEqual('0');
34132 expect(counter.getText()).toEqual('1');
34136 it('should throw and report an error when using "eval"', function() {
34138 expect(evilError.getText()).toMatch(/Content Security Policy/);
34139 expectError(/Content Security Policy/);
34145 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
34146 // bootstrap the system (before $parse is instantiated), for this reason we just have
34147 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
34154 * The ngClick directive allows you to specify custom behavior when
34155 * an element is clicked.
34159 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
34160 * click. ({@link guide/expression#-event- Event object is available as `$event`})
34164 <file name="index.html">
34165 <button ng-click="count = count + 1" ng-init="count=0">
34172 <file name="protractor.js" type="protractor">
34173 it('should check ng-click', function() {
34174 expect(element(by.binding('count')).getText()).toMatch('0');
34175 element(by.css('button')).click();
34176 expect(element(by.binding('count')).getText()).toMatch('1');
34182 * A collection of directives that allows creation of custom event handlers that are defined as
34183 * angular expressions and are compiled and executed within the current scope.
34185 var ngEventDirectives = {};
34187 // For events that might fire synchronously during DOM manipulation
34188 // we need to execute their event handlers asynchronously using $evalAsync,
34189 // so that they are not executed in an inconsistent state.
34190 var forceAsyncEvents = {
34195 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
34196 function(eventName) {
34197 var directiveName = directiveNormalize('ng-' + eventName);
34198 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
34201 compile: function($element, attr) {
34202 // We expose the powerful $event object on the scope that provides access to the Window,
34203 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
34204 // checks at the cost of speed since event handler expressions are not executed as
34205 // frequently as regular change detection.
34206 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
34207 return function ngEventHandler(scope, element) {
34208 element.on(eventName, function(event) {
34209 var callback = function() {
34210 fn(scope, {$event:event});
34212 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
34213 scope.$evalAsync(callback);
34215 scope.$apply(callback);
34230 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
34234 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
34235 * a dblclick. (The Event object is available as `$event`)
34239 <file name="index.html">
34240 <button ng-dblclick="count = count + 1" ng-init="count=0">
34241 Increment (on double click)
34251 * @name ngMousedown
34254 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
34258 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
34259 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
34263 <file name="index.html">
34264 <button ng-mousedown="count = count + 1" ng-init="count=0">
34265 Increment (on mouse down)
34278 * Specify custom behavior on mouseup event.
34282 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
34283 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
34287 <file name="index.html">
34288 <button ng-mouseup="count = count + 1" ng-init="count=0">
34289 Increment (on mouse up)
34298 * @name ngMouseover
34301 * Specify custom behavior on mouseover event.
34305 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
34306 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
34310 <file name="index.html">
34311 <button ng-mouseover="count = count + 1" ng-init="count=0">
34312 Increment (when mouse is over)
34322 * @name ngMouseenter
34325 * Specify custom behavior on mouseenter event.
34329 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
34330 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
34334 <file name="index.html">
34335 <button ng-mouseenter="count = count + 1" ng-init="count=0">
34336 Increment (when mouse enters)
34346 * @name ngMouseleave
34349 * Specify custom behavior on mouseleave event.
34353 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
34354 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
34358 <file name="index.html">
34359 <button ng-mouseleave="count = count + 1" ng-init="count=0">
34360 Increment (when mouse leaves)
34370 * @name ngMousemove
34373 * Specify custom behavior on mousemove event.
34377 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
34378 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
34382 <file name="index.html">
34383 <button ng-mousemove="count = count + 1" ng-init="count=0">
34384 Increment (when mouse moves)
34397 * Specify custom behavior on keydown event.
34401 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
34402 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
34406 <file name="index.html">
34407 <input ng-keydown="count = count + 1" ng-init="count=0">
34408 key down count: {{count}}
34419 * Specify custom behavior on keyup event.
34423 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
34424 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
34428 <file name="index.html">
34429 <p>Typing in the input box below updates the key count</p>
34430 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
34432 <p>Typing in the input box below updates the keycode</p>
34433 <input ng-keyup="event=$event">
34434 <p>event keyCode: {{ event.keyCode }}</p>
34435 <p>event altKey: {{ event.altKey }}</p>
34446 * Specify custom behavior on keypress event.
34449 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
34450 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
34451 * and can be interrogated for keyCode, altKey, etc.)
34455 <file name="index.html">
34456 <input ng-keypress="count = count + 1" ng-init="count=0">
34457 key press count: {{count}}
34468 * Enables binding angular expressions to onsubmit events.
34470 * Additionally it prevents the default action (which for form means sending the request to the
34471 * server and reloading the current page), but only if the form does not contain `action`,
34472 * `data-action`, or `x-action` attributes.
34474 * <div class="alert alert-warning">
34475 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
34476 * `ngSubmit` handlers together. See the
34477 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
34478 * for a detailed discussion of when `ngSubmit` may be triggered.
34483 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
34484 * ({@link guide/expression#-event- Event object is available as `$event`})
34487 <example module="submitExample">
34488 <file name="index.html">
34490 angular.module('submitExample', [])
34491 .controller('ExampleController', ['$scope', function($scope) {
34493 $scope.text = 'hello';
34494 $scope.submit = function() {
34496 $scope.list.push(this.text);
34502 <form ng-submit="submit()" ng-controller="ExampleController">
34503 Enter text and hit enter:
34504 <input type="text" ng-model="text" name="text" />
34505 <input type="submit" id="submit" value="Submit" />
34506 <pre>list={{list}}</pre>
34509 <file name="protractor.js" type="protractor">
34510 it('should check ng-submit', function() {
34511 expect(element(by.binding('list')).getText()).toBe('list=[]');
34512 element(by.css('#submit')).click();
34513 expect(element(by.binding('list')).getText()).toContain('hello');
34514 expect(element(by.model('text')).getAttribute('value')).toBe('');
34516 it('should ignore empty strings', function() {
34517 expect(element(by.binding('list')).getText()).toBe('list=[]');
34518 element(by.css('#submit')).click();
34519 element(by.css('#submit')).click();
34520 expect(element(by.binding('list')).getText()).toContain('hello');
34531 * Specify custom behavior on focus event.
34533 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
34534 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
34535 * during an `$apply` to ensure a consistent state.
34537 * @element window, input, select, textarea, a
34539 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
34540 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
34543 * See {@link ng.directive:ngClick ngClick}
34551 * Specify custom behavior on blur event.
34553 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
34554 * an element has lost focus.
34556 * Note: As the `blur` event is executed synchronously also during DOM manipulations
34557 * (e.g. removing a focussed input),
34558 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
34559 * during an `$apply` to ensure a consistent state.
34561 * @element window, input, select, textarea, a
34563 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
34564 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
34567 * See {@link ng.directive:ngClick ngClick}
34575 * Specify custom behavior on copy event.
34577 * @element window, input, select, textarea, a
34579 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
34580 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
34584 <file name="index.html">
34585 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
34596 * Specify custom behavior on cut event.
34598 * @element window, input, select, textarea, a
34600 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
34601 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
34605 <file name="index.html">
34606 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
34617 * Specify custom behavior on paste event.
34619 * @element window, input, select, textarea, a
34621 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
34622 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
34626 <file name="index.html">
34627 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
34640 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
34641 * {expression}. If the expression assigned to `ngIf` evaluates to a false
34642 * value then the element is removed from the DOM, otherwise a clone of the
34643 * element is reinserted into the DOM.
34645 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
34646 * element in the DOM rather than changing its visibility via the `display` css property. A common
34647 * case when this difference is significant is when using css selectors that rely on an element's
34648 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
34650 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
34651 * is created when the element is restored. The scope created within `ngIf` inherits from
34652 * its parent scope using
34653 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
34654 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
34655 * a javascript primitive defined in the parent scope. In this case any modifications made to the
34656 * variable within the child scope will override (hide) the value in the parent scope.
34658 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
34659 * is if an element's class attribute is directly modified after it's compiled, using something like
34660 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
34661 * the added class will be lost because the original compiled state is used to regenerate the element.
34663 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
34664 * and `leave` effects.
34667 * | Animation | Occurs |
34668 * |----------------------------------|-------------------------------------|
34669 * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
34670 * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
34675 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
34676 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
34677 * element is added to the DOM tree.
34680 <example module="ngAnimate" deps="angular-animate.js" animations="true">
34681 <file name="index.html">
34682 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
34684 <span ng-if="checked" class="animate-if">
34685 This is removed when the checkbox is unchecked.
34688 <file name="animations.css">
34691 border:1px solid black;
34695 .animate-if.ng-enter, .animate-if.ng-leave {
34696 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
34699 .animate-if.ng-enter,
34700 .animate-if.ng-leave.ng-leave-active {
34704 .animate-if.ng-leave,
34705 .animate-if.ng-enter.ng-enter-active {
34711 var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
34713 multiElement: true,
34714 transclude: 'element',
34719 link: function($scope, $element, $attr, ctrl, $transclude) {
34720 var block, childScope, previousElements;
34721 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
34725 $transclude(function(clone, newScope) {
34726 childScope = newScope;
34727 clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
34728 // Note: We only need the first/last node of the cloned nodes.
34729 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
34730 // by a directive with templateUrl when its template arrives.
34734 $animate.enter(clone, $element.parent(), $element);
34738 if (previousElements) {
34739 previousElements.remove();
34740 previousElements = null;
34743 childScope.$destroy();
34747 previousElements = getBlockNodes(block.clone);
34748 $animate.leave(previousElements).then(function() {
34749 previousElements = null;
34765 * Fetches, compiles and includes an external HTML fragment.
34767 * By default, the template URL is restricted to the same domain and protocol as the
34768 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
34769 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
34770 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
34771 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
34772 * ng.$sce Strict Contextual Escaping}.
34774 * In addition, the browser's
34775 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
34776 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
34777 * policy may further restrict whether the template is successfully loaded.
34778 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
34779 * access on some browsers.
34782 * | Animation | Occurs |
34783 * |----------------------------------|-------------------------------------|
34784 * | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
34785 * | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
34787 * The enter and leave animation occur concurrently.
34792 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
34793 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
34794 * @param {string=} onload Expression to evaluate when a new partial is loaded.
34795 * <div class="alert alert-warning">
34796 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
34797 * a function with the name on the window element, which will usually throw a
34798 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
34799 * different form that {@link guide/directive#normalization matches} `onload`.
34802 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
34803 * $anchorScroll} to scroll the viewport after the content is loaded.
34805 * - If the attribute is not set, disable scrolling.
34806 * - If the attribute is set without value, enable scrolling.
34807 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
34810 <example module="includeExample" deps="angular-animate.js" animations="true">
34811 <file name="index.html">
34812 <div ng-controller="ExampleController">
34813 <select ng-model="template" ng-options="t.name for t in templates">
34814 <option value="">(blank)</option>
34816 url of the template: <code>{{template.url}}</code>
34818 <div class="slide-animate-container">
34819 <div class="slide-animate" ng-include="template.url"></div>
34823 <file name="script.js">
34824 angular.module('includeExample', ['ngAnimate'])
34825 .controller('ExampleController', ['$scope', function($scope) {
34827 [ { name: 'template1.html', url: 'template1.html'},
34828 { name: 'template2.html', url: 'template2.html'} ];
34829 $scope.template = $scope.templates[0];
34832 <file name="template1.html">
34833 Content of template1.html
34835 <file name="template2.html">
34836 Content of template2.html
34838 <file name="animations.css">
34839 .slide-animate-container {
34842 border:1px solid black;
34851 .slide-animate.ng-enter, .slide-animate.ng-leave {
34852 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
34863 .slide-animate.ng-enter {
34866 .slide-animate.ng-enter.ng-enter-active {
34870 .slide-animate.ng-leave {
34873 .slide-animate.ng-leave.ng-leave-active {
34877 <file name="protractor.js" type="protractor">
34878 var templateSelect = element(by.model('template'));
34879 var includeElem = element(by.css('[ng-include]'));
34881 it('should load template1.html', function() {
34882 expect(includeElem.getText()).toMatch(/Content of template1.html/);
34885 it('should load template2.html', function() {
34886 if (browser.params.browser == 'firefox') {
34887 // Firefox can't handle using selects
34888 // See https://github.com/angular/protractor/issues/480
34891 templateSelect.click();
34892 templateSelect.all(by.css('option')).get(2).click();
34893 expect(includeElem.getText()).toMatch(/Content of template2.html/);
34896 it('should change to blank', function() {
34897 if (browser.params.browser == 'firefox') {
34898 // Firefox can't handle using selects
34901 templateSelect.click();
34902 templateSelect.all(by.css('option')).get(0).click();
34903 expect(includeElem.isPresent()).toBe(false);
34912 * @name ngInclude#$includeContentRequested
34913 * @eventType emit on the scope ngInclude was declared in
34915 * Emitted every time the ngInclude content is requested.
34917 * @param {Object} angularEvent Synthetic event object.
34918 * @param {String} src URL of content to load.
34924 * @name ngInclude#$includeContentLoaded
34925 * @eventType emit on the current ngInclude scope
34927 * Emitted every time the ngInclude content is reloaded.
34929 * @param {Object} angularEvent Synthetic event object.
34930 * @param {String} src URL of content to load.
34936 * @name ngInclude#$includeContentError
34937 * @eventType emit on the scope ngInclude was declared in
34939 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
34941 * @param {Object} angularEvent Synthetic event object.
34942 * @param {String} src URL of content to load.
34944 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
34945 function($templateRequest, $anchorScroll, $animate) {
34950 transclude: 'element',
34951 controller: angular.noop,
34952 compile: function(element, attr) {
34953 var srcExp = attr.ngInclude || attr.src,
34954 onloadExp = attr.onload || '',
34955 autoScrollExp = attr.autoscroll;
34957 return function(scope, $element, $attr, ctrl, $transclude) {
34958 var changeCounter = 0,
34963 var cleanupLastIncludeContent = function() {
34964 if (previousElement) {
34965 previousElement.remove();
34966 previousElement = null;
34968 if (currentScope) {
34969 currentScope.$destroy();
34970 currentScope = null;
34972 if (currentElement) {
34973 $animate.leave(currentElement).then(function() {
34974 previousElement = null;
34976 previousElement = currentElement;
34977 currentElement = null;
34981 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
34982 var afterAnimation = function() {
34983 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
34987 var thisChangeId = ++changeCounter;
34990 //set the 2nd param to true to ignore the template request error so that the inner
34991 //contents and scope can be cleaned up.
34992 $templateRequest(src, true).then(function(response) {
34993 if (scope.$$destroyed) return;
34995 if (thisChangeId !== changeCounter) return;
34996 var newScope = scope.$new();
34997 ctrl.template = response;
34999 // Note: This will also link all children of ng-include that were contained in the original
35000 // html. If that content contains controllers, ... they could pollute/change the scope.
35001 // However, using ng-include on an element with additional content does not make sense...
35002 // Note: We can't remove them in the cloneAttchFn of $transclude as that
35003 // function is called before linking the content, which would apply child
35004 // directives to non existing elements.
35005 var clone = $transclude(newScope, function(clone) {
35006 cleanupLastIncludeContent();
35007 $animate.enter(clone, null, $element).then(afterAnimation);
35010 currentScope = newScope;
35011 currentElement = clone;
35013 currentScope.$emit('$includeContentLoaded', src);
35014 scope.$eval(onloadExp);
35016 if (scope.$$destroyed) return;
35018 if (thisChangeId === changeCounter) {
35019 cleanupLastIncludeContent();
35020 scope.$emit('$includeContentError', src);
35023 scope.$emit('$includeContentRequested', src);
35025 cleanupLastIncludeContent();
35026 ctrl.template = null;
35034 // This directive is called during the $transclude call of the first `ngInclude` directive.
35035 // It will replace and compile the content of the element with the loaded template.
35036 // We need this directive so that the element content is already filled when
35037 // the link function of another directive on the same element as ngInclude
35039 var ngIncludeFillContentDirective = ['$compile',
35040 function($compile) {
35044 require: 'ngInclude',
35045 link: function(scope, $element, $attr, ctrl) {
35046 if (toString.call($element[0]).match(/SVG/)) {
35047 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
35048 // support innerHTML, so detect this here and try to generate the contents
35051 $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
35052 function namespaceAdaptedClone(clone) {
35053 $element.append(clone);
35054 }, {futureParentElement: $element});
35058 $element.html(ctrl.template);
35059 $compile($element.contents())(scope);
35070 * The `ngInit` directive allows you to evaluate an expression in the
35073 * <div class="alert alert-danger">
35074 * This directive can be abused to add unnecessary amounts of logic into your templates.
35075 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
35076 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
35077 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
35078 * rather than `ngInit` to initialize values on a scope.
35081 * <div class="alert alert-warning">
35082 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
35083 * sure you have parentheses to ensure correct operator precedence:
35084 * <pre class="prettyprint">
35085 * `<div ng-init="test1 = ($index | toString)"></div>`
35092 * @param {expression} ngInit {@link guide/expression Expression} to eval.
35095 <example module="initExample">
35096 <file name="index.html">
35098 angular.module('initExample', [])
35099 .controller('ExampleController', ['$scope', function($scope) {
35100 $scope.list = [['a', 'b'], ['c', 'd']];
35103 <div ng-controller="ExampleController">
35104 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
35105 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
35106 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
35111 <file name="protractor.js" type="protractor">
35112 it('should alias index positions', function() {
35113 var elements = element.all(by.css('.example-init'));
35114 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
35115 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
35116 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
35117 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
35122 var ngInitDirective = ngDirective({
35124 compile: function() {
35126 pre: function(scope, element, attrs) {
35127 scope.$eval(attrs.ngInit);
35138 * Text input that converts between a delimited string and an array of strings. The default
35139 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
35140 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
35142 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
35143 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
35144 * list item is respected. This implies that the user of the directive is responsible for
35145 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
35146 * tab or newline character.
35147 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
35148 * when joining the list items back together) and whitespace around each list item is stripped
35149 * before it is added to the model.
35151 * ### Example with Validation
35153 * <example name="ngList-directive" module="listExample">
35154 * <file name="app.js">
35155 * angular.module('listExample', [])
35156 * .controller('ExampleController', ['$scope', function($scope) {
35157 * $scope.names = ['morpheus', 'neo', 'trinity'];
35160 * <file name="index.html">
35161 * <form name="myForm" ng-controller="ExampleController">
35162 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
35163 * <span role="alert">
35164 * <span class="error" ng-show="myForm.namesInput.$error.required">
35168 * <tt>names = {{names}}</tt><br/>
35169 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
35170 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
35171 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
35172 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
35175 * <file name="protractor.js" type="protractor">
35176 * var listInput = element(by.model('names'));
35177 * var names = element(by.exactBinding('names'));
35178 * var valid = element(by.binding('myForm.namesInput.$valid'));
35179 * var error = element(by.css('span.error'));
35181 * it('should initialize to model', function() {
35182 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
35183 * expect(valid.getText()).toContain('true');
35184 * expect(error.getCssValue('display')).toBe('none');
35187 * it('should be invalid if empty', function() {
35188 * listInput.clear();
35189 * listInput.sendKeys('');
35191 * expect(names.getText()).toContain('');
35192 * expect(valid.getText()).toContain('false');
35193 * expect(error.getCssValue('display')).not.toBe('none');
35198 * ### Example - splitting on newline
35199 * <example name="ngList-directive-newlines">
35200 * <file name="index.html">
35201 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
35202 * <pre>{{ list | json }}</pre>
35204 * <file name="protractor.js" type="protractor">
35205 * it("should split the text by newlines", function() {
35206 * var listInput = element(by.model('list'));
35207 * var output = element(by.binding('list | json'));
35208 * listInput.sendKeys('abc\ndef\nghi');
35209 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
35215 * @param {string=} ngList optional delimiter that should be used to split the value.
35217 var ngListDirective = function() {
35221 require: 'ngModel',
35222 link: function(scope, element, attr, ctrl) {
35223 // We want to control whitespace trimming so we use this convoluted approach
35224 // to access the ngList attribute, which doesn't pre-trim the attribute
35225 var ngList = element.attr(attr.$attr.ngList) || ', ';
35226 var trimValues = attr.ngTrim !== 'false';
35227 var separator = trimValues ? trim(ngList) : ngList;
35229 var parse = function(viewValue) {
35230 // If the viewValue is invalid (say required but empty) it will be `undefined`
35231 if (isUndefined(viewValue)) return;
35236 forEach(viewValue.split(separator), function(value) {
35237 if (value) list.push(trimValues ? trim(value) : value);
35244 ctrl.$parsers.push(parse);
35245 ctrl.$formatters.push(function(value) {
35246 if (isArray(value)) {
35247 return value.join(ngList);
35253 // Override the standard $isEmpty because an empty array means the input is empty.
35254 ctrl.$isEmpty = function(value) {
35255 return !value || !value.length;
35261 /* global VALID_CLASS: true,
35262 INVALID_CLASS: true,
35263 PRISTINE_CLASS: true,
35265 UNTOUCHED_CLASS: true,
35266 TOUCHED_CLASS: true,
35269 var VALID_CLASS = 'ng-valid',
35270 INVALID_CLASS = 'ng-invalid',
35271 PRISTINE_CLASS = 'ng-pristine',
35272 DIRTY_CLASS = 'ng-dirty',
35273 UNTOUCHED_CLASS = 'ng-untouched',
35274 TOUCHED_CLASS = 'ng-touched',
35275 PENDING_CLASS = 'ng-pending',
35276 EMPTY_CLASS = 'ng-empty',
35277 NOT_EMPTY_CLASS = 'ng-not-empty';
35279 var ngModelMinErr = minErr('ngModel');
35283 * @name ngModel.NgModelController
35285 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
35286 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
35288 * @property {*} $modelValue The value in the model that the control is bound to.
35289 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
35290 the control reads value from the DOM. The functions are called in array order, each passing
35291 its return value through to the next. The last return value is forwarded to the
35292 {@link ngModel.NgModelController#$validators `$validators`} collection.
35294 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
35297 Returning `undefined` from a parser means a parse error occurred. In that case,
35298 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
35299 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
35300 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
35303 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
35304 the model value changes. The functions are called in reverse array order, each passing the value through to the
35305 next. The last return value is used as the actual DOM value.
35306 Used to format / convert values for display in the control.
35308 * function formatter(value) {
35310 * return value.toUpperCase();
35313 * ngModel.$formatters.push(formatter);
35316 * @property {Object.<string, function>} $validators A collection of validators that are applied
35317 * whenever the model value changes. The key value within the object refers to the name of the
35318 * validator while the function refers to the validation operation. The validation operation is
35319 * provided with the model value as an argument and must return a true or false value depending
35320 * on the response of that validation.
35323 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
35324 * var value = modelValue || viewValue;
35325 * return /[0-9]+/.test(value) &&
35326 * /[a-z]+/.test(value) &&
35327 * /[A-Z]+/.test(value) &&
35328 * /\W+/.test(value);
35332 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
35333 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
35334 * is expected to return a promise when it is run during the model validation process. Once the promise
35335 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
35336 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
35337 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
35338 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
35339 * will only run once all synchronous validators have passed.
35341 * Please note that if $http is used then it is important that the server returns a success HTTP response code
35342 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
35345 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
35346 * var value = modelValue || viewValue;
35348 * // Lookup user by username
35349 * return $http.get('/api/users/' + value).
35350 * then(function resolved() {
35351 * //username exists, this means validation fails
35352 * return $q.reject('exists');
35353 * }, function rejected() {
35354 * //username does not exist, therefore this validation passes
35360 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
35361 * view value has changed. It is called with no arguments, and its return value is ignored.
35362 * This can be used in place of additional $watches against the model value.
35364 * @property {Object} $error An object hash with all failing validator ids as keys.
35365 * @property {Object} $pending An object hash with all pending validator ids as keys.
35367 * @property {boolean} $untouched True if control has not lost focus yet.
35368 * @property {boolean} $touched True if control has lost focus.
35369 * @property {boolean} $pristine True if user has not interacted with the control yet.
35370 * @property {boolean} $dirty True if user has already interacted with the control.
35371 * @property {boolean} $valid True if there is no error.
35372 * @property {boolean} $invalid True if at least one error on the control.
35373 * @property {string} $name The name attribute of the control.
35377 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
35378 * The controller contains services for data-binding, validation, CSS updates, and value formatting
35379 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
35380 * listening to DOM events.
35381 * Such DOM related logic should be provided by other directives which make use of
35382 * `NgModelController` for data-binding to control elements.
35383 * Angular provides this DOM logic for most {@link input `input`} elements.
35384 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
35385 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
35388 * ### Custom Control Example
35389 * This example shows how to use `NgModelController` with a custom control to achieve
35390 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
35391 * collaborate together to achieve the desired result.
35393 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
35394 * contents be edited in place by the user.
35396 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
35397 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
35398 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
35399 * that content using the `$sce` service.
35401 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
35402 <file name="style.css">
35403 [contenteditable] {
35404 border: 1px solid black;
35405 background-color: white;
35410 border: 1px solid red;
35414 <file name="script.js">
35415 angular.module('customControl', ['ngSanitize']).
35416 directive('contenteditable', ['$sce', function($sce) {
35418 restrict: 'A', // only activate on element attribute
35419 require: '?ngModel', // get a hold of NgModelController
35420 link: function(scope, element, attrs, ngModel) {
35421 if (!ngModel) return; // do nothing if no ng-model
35423 // Specify how UI should be updated
35424 ngModel.$render = function() {
35425 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
35428 // Listen for change events to enable binding
35429 element.on('blur keyup change', function() {
35430 scope.$evalAsync(read);
35432 read(); // initialize
35434 // Write data to the model
35436 var html = element.html();
35437 // When we clear the content editable the browser leaves a <br> behind
35438 // If strip-br attribute is provided then we strip this out
35439 if ( attrs.stripBr && html == '<br>' ) {
35442 ngModel.$setViewValue(html);
35448 <file name="index.html">
35449 <form name="myForm">
35450 <div contenteditable
35451 name="myWidget" ng-model="userContent"
35453 required>Change me!</div>
35454 <span ng-show="myForm.myWidget.$error.required">Required!</span>
35456 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
35459 <file name="protractor.js" type="protractor">
35460 it('should data-bind and become invalid', function() {
35461 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
35462 // SafariDriver can't handle contenteditable
35463 // and Firefox driver can't clear contenteditables very well
35466 var contentEditable = element(by.css('[contenteditable]'));
35467 var content = 'Change me!';
35469 expect(contentEditable.getText()).toEqual(content);
35471 contentEditable.clear();
35472 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
35473 expect(contentEditable.getText()).toEqual('');
35474 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
35481 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
35482 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
35483 this.$viewValue = Number.NaN;
35484 this.$modelValue = Number.NaN;
35485 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
35486 this.$validators = {};
35487 this.$asyncValidators = {};
35488 this.$parsers = [];
35489 this.$formatters = [];
35490 this.$viewChangeListeners = [];
35491 this.$untouched = true;
35492 this.$touched = false;
35493 this.$pristine = true;
35494 this.$dirty = false;
35495 this.$valid = true;
35496 this.$invalid = false;
35497 this.$error = {}; // keep invalid keys here
35498 this.$$success = {}; // keep valid keys here
35499 this.$pending = undefined; // keep pending keys here
35500 this.$name = $interpolate($attr.name || '', false)($scope);
35501 this.$$parentForm = nullFormCtrl;
35503 var parsedNgModel = $parse($attr.ngModel),
35504 parsedNgModelAssign = parsedNgModel.assign,
35505 ngModelGet = parsedNgModel,
35506 ngModelSet = parsedNgModelAssign,
35507 pendingDebounce = null,
35511 this.$$setOptions = function(options) {
35512 ctrl.$options = options;
35513 if (options && options.getterSetter) {
35514 var invokeModelGetter = $parse($attr.ngModel + '()'),
35515 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
35517 ngModelGet = function($scope) {
35518 var modelValue = parsedNgModel($scope);
35519 if (isFunction(modelValue)) {
35520 modelValue = invokeModelGetter($scope);
35524 ngModelSet = function($scope, newValue) {
35525 if (isFunction(parsedNgModel($scope))) {
35526 invokeModelSetter($scope, {$$$p: newValue});
35528 parsedNgModelAssign($scope, newValue);
35531 } else if (!parsedNgModel.assign) {
35532 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
35533 $attr.ngModel, startingTag($element));
35539 * @name ngModel.NgModelController#$render
35542 * Called when the view needs to be updated. It is expected that the user of the ng-model
35543 * directive will implement this method.
35545 * The `$render()` method is invoked in the following situations:
35547 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
35548 * committed value then `$render()` is called to update the input control.
35549 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
35550 * the `$viewValue` are different from last time.
35552 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
35553 * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
35554 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
35555 * invoked if you only change a property on the objects.
35557 this.$render = noop;
35561 * @name ngModel.NgModelController#$isEmpty
35564 * This is called when we need to determine if the value of an input is empty.
35566 * For instance, the required directive does this to work out if the input has data or not.
35568 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
35570 * You can override this for input directives whose concept of being empty is different from the
35571 * default. The `checkboxInputType` directive does this because in its case a value of `false`
35574 * @param {*} value The value of the input to check for emptiness.
35575 * @returns {boolean} True if `value` is "empty".
35577 this.$isEmpty = function(value) {
35578 return isUndefined(value) || value === '' || value === null || value !== value;
35581 this.$$updateEmptyClasses = function(value) {
35582 if (ctrl.$isEmpty(value)) {
35583 $animate.removeClass($element, NOT_EMPTY_CLASS);
35584 $animate.addClass($element, EMPTY_CLASS);
35586 $animate.removeClass($element, EMPTY_CLASS);
35587 $animate.addClass($element, NOT_EMPTY_CLASS);
35592 var currentValidationRunId = 0;
35596 * @name ngModel.NgModelController#$setValidity
35599 * Change the validity state, and notify the form.
35601 * This method can be called within $parsers/$formatters or a custom validation implementation.
35602 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
35603 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
35605 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
35606 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
35607 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
35608 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
35609 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
35610 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
35611 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
35612 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
35613 * Skipped is used by Angular when validators do not run because of parse errors and
35614 * when `$asyncValidators` do not run because any of the `$validators` failed.
35616 addSetValidityMethod({
35618 $element: $element,
35619 set: function(object, property) {
35620 object[property] = true;
35622 unset: function(object, property) {
35623 delete object[property];
35630 * @name ngModel.NgModelController#$setPristine
35633 * Sets the control to its pristine state.
35635 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
35636 * state (`ng-pristine` class). A model is considered to be pristine when the control
35637 * has not been changed from when first compiled.
35639 this.$setPristine = function() {
35640 ctrl.$dirty = false;
35641 ctrl.$pristine = true;
35642 $animate.removeClass($element, DIRTY_CLASS);
35643 $animate.addClass($element, PRISTINE_CLASS);
35648 * @name ngModel.NgModelController#$setDirty
35651 * Sets the control to its dirty state.
35653 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
35654 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
35655 * from when first compiled.
35657 this.$setDirty = function() {
35658 ctrl.$dirty = true;
35659 ctrl.$pristine = false;
35660 $animate.removeClass($element, PRISTINE_CLASS);
35661 $animate.addClass($element, DIRTY_CLASS);
35662 ctrl.$$parentForm.$setDirty();
35667 * @name ngModel.NgModelController#$setUntouched
35670 * Sets the control to its untouched state.
35672 * This method can be called to remove the `ng-touched` class and set the control to its
35673 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
35674 * by default, however this function can be used to restore that state if the model has
35675 * already been touched by the user.
35677 this.$setUntouched = function() {
35678 ctrl.$touched = false;
35679 ctrl.$untouched = true;
35680 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
35685 * @name ngModel.NgModelController#$setTouched
35688 * Sets the control to its touched state.
35690 * This method can be called to remove the `ng-untouched` class and set the control to its
35691 * touched state (`ng-touched` class). A model is considered to be touched when the user has
35692 * first focused the control element and then shifted focus away from the control (blur event).
35694 this.$setTouched = function() {
35695 ctrl.$touched = true;
35696 ctrl.$untouched = false;
35697 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
35702 * @name ngModel.NgModelController#$rollbackViewValue
35705 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
35706 * which may be caused by a pending debounced event or because the input is waiting for a some
35709 * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
35710 * depend on special events such as blur, you can have a situation where there is a period when
35711 * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
35713 * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
35714 * and reset the input to the last committed view value.
35716 * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
35717 * programmatically before these debounced/future events have resolved/occurred, because Angular's
35718 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
35720 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
35721 * input which may have such events pending. This is important in order to make sure that the
35722 * input field will be updated with the new model value and any pending operations are cancelled.
35724 * <example name="ng-model-cancel-update" module="cancel-update-example">
35725 * <file name="app.js">
35726 * angular.module('cancel-update-example', [])
35728 * .controller('CancelUpdateController', ['$scope', function($scope) {
35729 * $scope.model = {};
35731 * $scope.setEmpty = function(e, value, rollback) {
35732 * if (e.keyCode == 27) {
35733 * e.preventDefault();
35735 * $scope.myForm[value].$rollbackViewValue();
35737 * $scope.model[value] = '';
35742 * <file name="index.html">
35743 * <div ng-controller="CancelUpdateController">
35744 * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
35745 * empty them. Follow these steps and observe the difference:</p>
35747 * <li>Type something in the input. You will see that the model is not yet updated</li>
35748 * <li>Press the Escape key.
35750 * <li> In the first example, nothing happens, because the model is already '', and no
35751 * update is detected. If you blur the input, the model will be set to the current view.
35753 * <li> In the second example, the pending update is cancelled, and the input is set back
35754 * to the last committed view value (''). Blurring the input does nothing.
35760 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
35762 * <p id="inputDescription1">Without $rollbackViewValue():</p>
35763 * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
35764 * ng-keydown="setEmpty($event, 'value1')">
35765 * value1: "{{ model.value1 }}"
35769 * <p id="inputDescription2">With $rollbackViewValue():</p>
35770 * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
35771 * ng-keydown="setEmpty($event, 'value2', true)">
35772 * value2: "{{ model.value2 }}"
35777 <file name="style.css">
35779 display: table-cell;
35782 padding-right: 30px;
35788 this.$rollbackViewValue = function() {
35789 $timeout.cancel(pendingDebounce);
35790 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
35796 * @name ngModel.NgModelController#$validate
35799 * Runs each of the registered validators (first synchronous validators and then
35800 * asynchronous validators).
35801 * If the validity changes to invalid, the model will be set to `undefined`,
35802 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
35803 * If the validity changes to valid, it will set the model to the last available valid
35804 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
35806 this.$validate = function() {
35807 // ignore $validate before model is initialized
35808 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
35812 var viewValue = ctrl.$$lastCommittedViewValue;
35813 // Note: we use the $$rawModelValue as $modelValue might have been
35814 // set to undefined during a view -> model update that found validation
35815 // errors. We can't parse the view here, since that could change
35816 // the model although neither viewValue nor the model on the scope changed
35817 var modelValue = ctrl.$$rawModelValue;
35819 var prevValid = ctrl.$valid;
35820 var prevModelValue = ctrl.$modelValue;
35822 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
35824 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
35825 // If there was no change in validity, don't update the model
35826 // This prevents changing an invalid modelValue to undefined
35827 if (!allowInvalid && prevValid !== allValid) {
35828 // Note: Don't check ctrl.$valid here, as we could have
35829 // external validators (e.g. calculated on the server),
35830 // that just call $setValidity and need the model value
35831 // to calculate their validity.
35832 ctrl.$modelValue = allValid ? modelValue : undefined;
35834 if (ctrl.$modelValue !== prevModelValue) {
35835 ctrl.$$writeModelToScope();
35842 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
35843 currentValidationRunId++;
35844 var localValidationRunId = currentValidationRunId;
35846 // check parser error
35847 if (!processParseErrors()) {
35848 validationDone(false);
35851 if (!processSyncValidators()) {
35852 validationDone(false);
35855 processAsyncValidators();
35857 function processParseErrors() {
35858 var errorKey = ctrl.$$parserName || 'parse';
35859 if (isUndefined(parserValid)) {
35860 setValidity(errorKey, null);
35862 if (!parserValid) {
35863 forEach(ctrl.$validators, function(v, name) {
35864 setValidity(name, null);
35866 forEach(ctrl.$asyncValidators, function(v, name) {
35867 setValidity(name, null);
35870 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
35871 setValidity(errorKey, parserValid);
35872 return parserValid;
35877 function processSyncValidators() {
35878 var syncValidatorsValid = true;
35879 forEach(ctrl.$validators, function(validator, name) {
35880 var result = validator(modelValue, viewValue);
35881 syncValidatorsValid = syncValidatorsValid && result;
35882 setValidity(name, result);
35884 if (!syncValidatorsValid) {
35885 forEach(ctrl.$asyncValidators, function(v, name) {
35886 setValidity(name, null);
35893 function processAsyncValidators() {
35894 var validatorPromises = [];
35895 var allValid = true;
35896 forEach(ctrl.$asyncValidators, function(validator, name) {
35897 var promise = validator(modelValue, viewValue);
35898 if (!isPromiseLike(promise)) {
35899 throw ngModelMinErr('nopromise',
35900 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
35902 setValidity(name, undefined);
35903 validatorPromises.push(promise.then(function() {
35904 setValidity(name, true);
35907 setValidity(name, false);
35910 if (!validatorPromises.length) {
35911 validationDone(true);
35913 $q.all(validatorPromises).then(function() {
35914 validationDone(allValid);
35919 function setValidity(name, isValid) {
35920 if (localValidationRunId === currentValidationRunId) {
35921 ctrl.$setValidity(name, isValid);
35925 function validationDone(allValid) {
35926 if (localValidationRunId === currentValidationRunId) {
35928 doneCallback(allValid);
35935 * @name ngModel.NgModelController#$commitViewValue
35938 * Commit a pending update to the `$modelValue`.
35940 * Updates may be pending by a debounced event or because the input is waiting for a some future
35941 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
35942 * usually handles calling this in response to input events.
35944 this.$commitViewValue = function() {
35945 var viewValue = ctrl.$viewValue;
35947 $timeout.cancel(pendingDebounce);
35949 // If the view value has not changed then we should just exit, except in the case where there is
35950 // a native validator on the element. In this case the validation state may have changed even though
35951 // the viewValue has stayed empty.
35952 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
35955 ctrl.$$updateEmptyClasses(viewValue);
35956 ctrl.$$lastCommittedViewValue = viewValue;
35959 if (ctrl.$pristine) {
35962 this.$$parseAndValidate();
35965 this.$$parseAndValidate = function() {
35966 var viewValue = ctrl.$$lastCommittedViewValue;
35967 var modelValue = viewValue;
35968 parserValid = isUndefined(modelValue) ? undefined : true;
35971 for (var i = 0; i < ctrl.$parsers.length; i++) {
35972 modelValue = ctrl.$parsers[i](modelValue);
35973 if (isUndefined(modelValue)) {
35974 parserValid = false;
35979 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
35980 // ctrl.$modelValue has not been touched yet...
35981 ctrl.$modelValue = ngModelGet($scope);
35983 var prevModelValue = ctrl.$modelValue;
35984 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
35985 ctrl.$$rawModelValue = modelValue;
35987 if (allowInvalid) {
35988 ctrl.$modelValue = modelValue;
35989 writeToModelIfNeeded();
35992 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
35993 // This can happen if e.g. $setViewValue is called from inside a parser
35994 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
35995 if (!allowInvalid) {
35996 // Note: Don't check ctrl.$valid here, as we could have
35997 // external validators (e.g. calculated on the server),
35998 // that just call $setValidity and need the model value
35999 // to calculate their validity.
36000 ctrl.$modelValue = allValid ? modelValue : undefined;
36001 writeToModelIfNeeded();
36005 function writeToModelIfNeeded() {
36006 if (ctrl.$modelValue !== prevModelValue) {
36007 ctrl.$$writeModelToScope();
36012 this.$$writeModelToScope = function() {
36013 ngModelSet($scope, ctrl.$modelValue);
36014 forEach(ctrl.$viewChangeListeners, function(listener) {
36018 $exceptionHandler(e);
36025 * @name ngModel.NgModelController#$setViewValue
36028 * Update the view value.
36030 * This method should be called when a control wants to change the view value; typically,
36031 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
36032 * directive calls it when the value of the input changes and {@link ng.directive:select select}
36033 * calls it when an option is selected.
36035 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
36036 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
36037 * value sent directly for processing, finally to be applied to `$modelValue` and then the
36038 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
36039 * in the `$viewChangeListeners` list, are called.
36041 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
36042 * and the `default` trigger is not listed, all those actions will remain pending until one of the
36043 * `updateOn` events is triggered on the DOM element.
36044 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
36045 * directive is used with a custom debounce for this particular event.
36046 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
36047 * is specified, once the timer runs out.
36049 * When used with standard inputs, the view value will always be a string (which is in some cases
36050 * parsed into another type, such as a `Date` object for `input[date]`.)
36051 * However, custom controls might also pass objects to this method. In this case, we should make
36052 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
36053 * perform a deep watch of objects, it only looks for a change of identity. If you only change
36054 * the property of the object then ngModel will not realize that the object has changed and
36055 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
36056 * not change properties of the copy once it has been passed to `$setViewValue`.
36057 * Otherwise you may cause the model value on the scope to change incorrectly.
36059 * <div class="alert alert-info">
36060 * In any case, the value passed to the method should always reflect the current value
36061 * of the control. For example, if you are calling `$setViewValue` for an input element,
36062 * you should pass the input DOM value. Otherwise, the control and the scope model become
36063 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
36064 * the control's DOM value in any way. If we want to change the control's DOM value
36065 * programmatically, we should update the `ngModel` scope expression. Its new value will be
36066 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
36067 * to update the DOM, and finally call `$validate` on it.
36070 * @param {*} value value from the view.
36071 * @param {string} trigger Event that triggered the update.
36073 this.$setViewValue = function(value, trigger) {
36074 ctrl.$viewValue = value;
36075 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
36076 ctrl.$$debounceViewValueCommit(trigger);
36080 this.$$debounceViewValueCommit = function(trigger) {
36081 var debounceDelay = 0,
36082 options = ctrl.$options,
36085 if (options && isDefined(options.debounce)) {
36086 debounce = options.debounce;
36087 if (isNumber(debounce)) {
36088 debounceDelay = debounce;
36089 } else if (isNumber(debounce[trigger])) {
36090 debounceDelay = debounce[trigger];
36091 } else if (isNumber(debounce['default'])) {
36092 debounceDelay = debounce['default'];
36096 $timeout.cancel(pendingDebounce);
36097 if (debounceDelay) {
36098 pendingDebounce = $timeout(function() {
36099 ctrl.$commitViewValue();
36101 } else if ($rootScope.$$phase) {
36102 ctrl.$commitViewValue();
36104 $scope.$apply(function() {
36105 ctrl.$commitViewValue();
36111 // Note: we cannot use a normal scope.$watch as we want to detect the following:
36112 // 1. scope value is 'a'
36113 // 2. user enters 'b'
36114 // 3. ng-change kicks in and reverts scope value to 'a'
36115 // -> scope value did not change since the last digest as
36116 // ng-change executes in apply phase
36117 // 4. view should be changed back to 'a'
36118 $scope.$watch(function ngModelWatch() {
36119 var modelValue = ngModelGet($scope);
36121 // if scope model value and ngModel value are out of sync
36122 // TODO(perf): why not move this to the action fn?
36123 if (modelValue !== ctrl.$modelValue &&
36124 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
36125 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
36127 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
36128 parserValid = undefined;
36130 var formatters = ctrl.$formatters,
36131 idx = formatters.length;
36133 var viewValue = modelValue;
36135 viewValue = formatters[idx](viewValue);
36137 if (ctrl.$viewValue !== viewValue) {
36138 ctrl.$$updateEmptyClasses(viewValue);
36139 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
36142 ctrl.$$runValidators(modelValue, viewValue, noop);
36159 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
36160 * property on the scope using {@link ngModel.NgModelController NgModelController},
36161 * which is created and exposed by this directive.
36163 * `ngModel` is responsible for:
36165 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
36167 * - Providing validation behavior (i.e. required, number, email, url).
36168 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
36169 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
36170 * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
36171 * - Registering the control with its parent {@link ng.directive:form form}.
36173 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
36174 * current scope. If the property doesn't already exist on this scope, it will be created
36175 * implicitly and added to the scope.
36177 * For best practices on using `ngModel`, see:
36179 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
36181 * For basic examples, how to use `ngModel`, see:
36183 * - {@link ng.directive:input input}
36184 * - {@link input[text] text}
36185 * - {@link input[checkbox] checkbox}
36186 * - {@link input[radio] radio}
36187 * - {@link input[number] number}
36188 * - {@link input[email] email}
36189 * - {@link input[url] url}
36190 * - {@link input[date] date}
36191 * - {@link input[datetime-local] datetime-local}
36192 * - {@link input[time] time}
36193 * - {@link input[month] month}
36194 * - {@link input[week] week}
36195 * - {@link ng.directive:select select}
36196 * - {@link ng.directive:textarea textarea}
36198 * # Complex Models (objects or collections)
36200 * By default, `ngModel` watches the model by reference, not value. This is important to know when
36201 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
36202 * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
36204 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
36206 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
36207 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
36208 * if the select is given the `multiple` attribute.
36210 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
36211 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
36212 * not trigger a re-rendering of the model.
36215 * The following CSS classes are added and removed on the associated input/select/textarea element
36216 * depending on the validity of the model.
36218 * - `ng-valid`: the model is valid
36219 * - `ng-invalid`: the model is invalid
36220 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
36221 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
36222 * - `ng-pristine`: the control hasn't been interacted with yet
36223 * - `ng-dirty`: the control has been interacted with
36224 * - `ng-touched`: the control has been blurred
36225 * - `ng-untouched`: the control hasn't been blurred
36226 * - `ng-pending`: any `$asyncValidators` are unfulfilled
36227 * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
36228 * by the {@link ngModel.NgModelController#$isEmpty} method
36229 * - `ng-not-empty`: the view contains a non-empty value
36231 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
36233 * ## Animation Hooks
36235 * Animations within models are triggered when any of the associated CSS classes are added and removed
36236 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
36237 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
36238 * The animations that are triggered within ngModel are similar to how they work in ngClass and
36239 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
36241 * The following example shows a simple way to utilize CSS transitions to style an input element
36242 * that has been rendered as invalid after it has been validated:
36245 * //be sure to include ngAnimate as a module to hook into more
36246 * //advanced animations
36248 * transition:0.5s linear all;
36249 * background: white;
36251 * .my-input.ng-invalid {
36258 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
36259 <file name="index.html">
36261 angular.module('inputExample', [])
36262 .controller('ExampleController', ['$scope', function($scope) {
36268 transition:all linear 0.5s;
36269 background: transparent;
36271 .my-input.ng-invalid {
36276 <p id="inputDescription">
36277 Update input to see transitions when valid/invalid.
36278 Integer is a valid value.
36280 <form name="testForm" ng-controller="ExampleController">
36281 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
36282 aria-describedby="inputDescription" />
36287 * ## Binding to a getter/setter
36289 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
36290 * function that returns a representation of the model when called with zero arguments, and sets
36291 * the internal state of a model when called with an argument. It's sometimes useful to use this
36292 * for models that have an internal representation that's different from what the model exposes
36295 * <div class="alert alert-success">
36296 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
36297 * frequently than other parts of your code.
36300 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
36301 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
36302 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
36303 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
36305 * The following example shows how to use `ngModel` with a getter/setter:
36308 * <example name="ngModel-getter-setter" module="getterSetterExample">
36309 <file name="index.html">
36310 <div ng-controller="ExampleController">
36311 <form name="userForm">
36313 <input type="text" name="userName"
36314 ng-model="user.name"
36315 ng-model-options="{ getterSetter: true }" />
36318 <pre>user.name = <span ng-bind="user.name()"></span></pre>
36321 <file name="app.js">
36322 angular.module('getterSetterExample', [])
36323 .controller('ExampleController', ['$scope', function($scope) {
36324 var _name = 'Brian';
36326 name: function(newName) {
36327 // Note that newName can be undefined for two reasons:
36328 // 1. Because it is called as a getter and thus called with no arguments
36329 // 2. Because the property should actually be set to undefined. This happens e.g. if the
36330 // input is invalid
36331 return arguments.length ? (_name = newName) : _name;
36338 var ngModelDirective = ['$rootScope', function($rootScope) {
36341 require: ['ngModel', '^?form', '^?ngModelOptions'],
36342 controller: NgModelController,
36343 // Prelink needs to run before any input directive
36344 // so that we can set the NgModelOptions in NgModelController
36345 // before anyone else uses it.
36347 compile: function ngModelCompile(element) {
36348 // Setup initial state of the control
36349 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
36352 pre: function ngModelPreLink(scope, element, attr, ctrls) {
36353 var modelCtrl = ctrls[0],
36354 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
36356 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
36358 // notify others, especially parent forms
36359 formCtrl.$addControl(modelCtrl);
36361 attr.$observe('name', function(newValue) {
36362 if (modelCtrl.$name !== newValue) {
36363 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
36367 scope.$on('$destroy', function() {
36368 modelCtrl.$$parentForm.$removeControl(modelCtrl);
36371 post: function ngModelPostLink(scope, element, attr, ctrls) {
36372 var modelCtrl = ctrls[0];
36373 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
36374 element.on(modelCtrl.$options.updateOn, function(ev) {
36375 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
36379 element.on('blur', function() {
36380 if (modelCtrl.$touched) return;
36382 if ($rootScope.$$phase) {
36383 scope.$evalAsync(modelCtrl.$setTouched);
36385 scope.$apply(modelCtrl.$setTouched);
36394 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
36398 * @name ngModelOptions
36401 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
36402 * events that will trigger a model update and/or a debouncing delay so that the actual update only
36403 * takes place when a timer expires; this timer will be reset after another change takes place.
36405 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
36406 * be different from the value in the actual model. This means that if you update the model you
36407 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
36408 * order to make sure it is synchronized with the model and that any debounced action is canceled.
36410 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
36411 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
36412 * important because `form` controllers are published to the related scope under the name in their
36413 * `name` attribute.
36415 * Any pending changes will take place immediately when an enclosing form is submitted via the
36416 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
36417 * to have access to the updated model.
36419 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
36421 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
36422 * - `updateOn`: string specifying which event should the input be bound to. You can set several
36423 * events using an space delimited list. There is a special event called `default` that
36424 * matches the default events belonging of the control.
36425 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
36426 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
36427 * custom value for each event. For example:
36428 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
36429 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
36430 * not validate correctly instead of the default behavior of setting the model to undefined.
36431 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
36432 `ngModel` as getters/setters.
36433 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
36434 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
36435 * continental US time zone abbreviations, but for general use, use a time zone offset, for
36436 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
36437 * If not specified, the timezone of the browser will be used.
36441 The following example shows how to override immediate updates. Changes on the inputs within the
36442 form will update the model only when the control loses focus (blur event). If `escape` key is
36443 pressed while the input field is focused, the value is reset to the value in the current model.
36445 <example name="ngModelOptions-directive-blur" module="optionsExample">
36446 <file name="index.html">
36447 <div ng-controller="ExampleController">
36448 <form name="userForm">
36450 <input type="text" name="userName"
36451 ng-model="user.name"
36452 ng-model-options="{ updateOn: 'blur' }"
36453 ng-keyup="cancel($event)" />
36456 <input type="text" ng-model="user.data" />
36459 <pre>user.name = <span ng-bind="user.name"></span></pre>
36460 <pre>user.data = <span ng-bind="user.data"></span></pre>
36463 <file name="app.js">
36464 angular.module('optionsExample', [])
36465 .controller('ExampleController', ['$scope', function($scope) {
36466 $scope.user = { name: 'John', data: '' };
36468 $scope.cancel = function(e) {
36469 if (e.keyCode == 27) {
36470 $scope.userForm.userName.$rollbackViewValue();
36475 <file name="protractor.js" type="protractor">
36476 var model = element(by.binding('user.name'));
36477 var input = element(by.model('user.name'));
36478 var other = element(by.model('user.data'));
36480 it('should allow custom events', function() {
36481 input.sendKeys(' Doe');
36483 expect(model.getText()).toEqual('John');
36485 expect(model.getText()).toEqual('John Doe');
36488 it('should $rollbackViewValue when model changes', function() {
36489 input.sendKeys(' Doe');
36490 expect(input.getAttribute('value')).toEqual('John Doe');
36491 input.sendKeys(protractor.Key.ESCAPE);
36492 expect(input.getAttribute('value')).toEqual('John');
36494 expect(model.getText()).toEqual('John');
36499 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
36500 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
36502 <example name="ngModelOptions-directive-debounce" module="optionsExample">
36503 <file name="index.html">
36504 <div ng-controller="ExampleController">
36505 <form name="userForm">
36507 <input type="text" name="userName"
36508 ng-model="user.name"
36509 ng-model-options="{ debounce: 1000 }" />
36511 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
36514 <pre>user.name = <span ng-bind="user.name"></span></pre>
36517 <file name="app.js">
36518 angular.module('optionsExample', [])
36519 .controller('ExampleController', ['$scope', function($scope) {
36520 $scope.user = { name: 'Igor' };
36525 This one shows how to bind to getter/setters:
36527 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
36528 <file name="index.html">
36529 <div ng-controller="ExampleController">
36530 <form name="userForm">
36532 <input type="text" name="userName"
36533 ng-model="user.name"
36534 ng-model-options="{ getterSetter: true }" />
36537 <pre>user.name = <span ng-bind="user.name()"></span></pre>
36540 <file name="app.js">
36541 angular.module('getterSetterExample', [])
36542 .controller('ExampleController', ['$scope', function($scope) {
36543 var _name = 'Brian';
36545 name: function(newName) {
36546 // Note that newName can be undefined for two reasons:
36547 // 1. Because it is called as a getter and thus called with no arguments
36548 // 2. Because the property should actually be set to undefined. This happens e.g. if the
36549 // input is invalid
36550 return arguments.length ? (_name = newName) : _name;
36557 var ngModelOptionsDirective = function() {
36560 controller: ['$scope', '$attrs', function($scope, $attrs) {
36562 this.$options = copy($scope.$eval($attrs.ngModelOptions));
36563 // Allow adding/overriding bound events
36564 if (isDefined(this.$options.updateOn)) {
36565 this.$options.updateOnDefault = false;
36566 // extract "default" pseudo-event from list of events that can trigger a model update
36567 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
36568 that.$options.updateOnDefault = true;
36572 this.$options.updateOnDefault = true;
36581 function addSetValidityMethod(context) {
36582 var ctrl = context.ctrl,
36583 $element = context.$element,
36586 unset = context.unset,
36587 $animate = context.$animate;
36589 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
36591 ctrl.$setValidity = setValidity;
36593 function setValidity(validationErrorKey, state, controller) {
36594 if (isUndefined(state)) {
36595 createAndSet('$pending', validationErrorKey, controller);
36597 unsetAndCleanup('$pending', validationErrorKey, controller);
36599 if (!isBoolean(state)) {
36600 unset(ctrl.$error, validationErrorKey, controller);
36601 unset(ctrl.$$success, validationErrorKey, controller);
36604 unset(ctrl.$error, validationErrorKey, controller);
36605 set(ctrl.$$success, validationErrorKey, controller);
36607 set(ctrl.$error, validationErrorKey, controller);
36608 unset(ctrl.$$success, validationErrorKey, controller);
36611 if (ctrl.$pending) {
36612 cachedToggleClass(PENDING_CLASS, true);
36613 ctrl.$valid = ctrl.$invalid = undefined;
36614 toggleValidationCss('', null);
36616 cachedToggleClass(PENDING_CLASS, false);
36617 ctrl.$valid = isObjectEmpty(ctrl.$error);
36618 ctrl.$invalid = !ctrl.$valid;
36619 toggleValidationCss('', ctrl.$valid);
36622 // re-read the state as the set/unset methods could have
36623 // combined state in ctrl.$error[validationError] (used for forms),
36624 // where setting/unsetting only increments/decrements the value,
36625 // and does not replace it.
36627 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
36628 combinedState = undefined;
36629 } else if (ctrl.$error[validationErrorKey]) {
36630 combinedState = false;
36631 } else if (ctrl.$$success[validationErrorKey]) {
36632 combinedState = true;
36634 combinedState = null;
36637 toggleValidationCss(validationErrorKey, combinedState);
36638 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
36641 function createAndSet(name, value, controller) {
36645 set(ctrl[name], value, controller);
36648 function unsetAndCleanup(name, value, controller) {
36650 unset(ctrl[name], value, controller);
36652 if (isObjectEmpty(ctrl[name])) {
36653 ctrl[name] = undefined;
36657 function cachedToggleClass(className, switchValue) {
36658 if (switchValue && !classCache[className]) {
36659 $animate.addClass($element, className);
36660 classCache[className] = true;
36661 } else if (!switchValue && classCache[className]) {
36662 $animate.removeClass($element, className);
36663 classCache[className] = false;
36667 function toggleValidationCss(validationErrorKey, isValid) {
36668 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
36670 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
36671 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
36675 function isObjectEmpty(obj) {
36677 for (var prop in obj) {
36678 if (obj.hasOwnProperty(prop)) {
36688 * @name ngNonBindable
36693 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
36694 * DOM element. This is useful if the element contains what appears to be Angular directives and
36695 * bindings but which should be ignored by Angular. This could be the case if you have a site that
36696 * displays snippets of code, for instance.
36701 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
36702 * but the one wrapped in `ngNonBindable` is left alone.
36706 <file name="index.html">
36707 <div>Normal: {{1 + 2}}</div>
36708 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
36710 <file name="protractor.js" type="protractor">
36711 it('should check ng-non-bindable', function() {
36712 expect(element(by.binding('1 + 2')).getText()).toContain('3');
36713 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
36718 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
36720 /* global jqLiteRemove */
36722 var ngOptionsMinErr = minErr('ngOptions');
36731 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
36732 * elements for the `<select>` element using the array or object obtained by evaluating the
36733 * `ngOptions` comprehension expression.
36735 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
36736 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
36737 * increasing speed by not creating a new scope for each repeated instance, as well as providing
36738 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
36739 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
36740 * to a non-string value. This is because an option element can only be bound to string values at
36743 * When an item in the `<select>` menu is selected, the array element or object property
36744 * represented by the selected option will be bound to the model identified by the `ngModel`
36747 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
36748 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
36749 * option. See example below for demonstration.
36751 * ## Complex Models (objects or collections)
36753 * By default, `ngModel` watches the model by reference, not value. This is important to know when
36754 * binding the select to a model that is an object or a collection.
36756 * One issue occurs if you want to preselect an option. For example, if you set
36757 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
36758 * because the objects are not identical. So by default, you should always reference the item in your collection
36759 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
36761 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
36762 * of the item not by reference, but by the result of the `track by` expression. For example, if your
36763 * collection items have an id property, you would `track by item.id`.
36765 * A different issue with objects or collections is that ngModel won't detect if an object property or
36766 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
36767 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
36768 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
36769 * has not changed identity, but only a property on the object or an item in the collection changes.
36771 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
36772 * if the model is an array). This means that changing a property deeper than the first level inside the
36773 * object/collection will not trigger a re-rendering.
36775 * ## `select` **`as`**
36777 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
36778 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
36779 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
36780 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
36783 * ### `select` **`as`** and **`track by`**
36785 * <div class="alert alert-warning">
36786 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
36789 * Given this array of items on the $scope:
36792 * $scope.items = [{
36795 * subItem: { name: 'aSubItem' }
36799 * subItem: { name: 'bSubItem' }
36806 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
36809 * $scope.selected = $scope.items[0];
36812 * but this will not work:
36815 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
36818 * $scope.selected = $scope.items[0].subItem;
36821 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
36822 * `items` array. Because the selected option has been set programmatically in the controller, the
36823 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
36824 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
36825 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
36826 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
36827 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
36830 * @param {string} ngModel Assignable angular expression to data-bind to.
36831 * @param {string=} name Property name of the form under which the control is published.
36832 * @param {string=} required The control is considered valid only if value is entered.
36833 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
36834 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
36835 * `required` when you want to data-bind to the `required` attribute.
36836 * @param {comprehension_expression=} ngOptions in one of the following forms:
36838 * * for array data sources:
36839 * * `label` **`for`** `value` **`in`** `array`
36840 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
36841 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
36842 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
36843 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
36844 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
36845 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
36846 * (for including a filter with `track by`)
36847 * * for object data sources:
36848 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
36849 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
36850 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
36851 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
36852 * * `select` **`as`** `label` **`group by`** `group`
36853 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
36854 * * `select` **`as`** `label` **`disable when`** `disable`
36855 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
36859 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
36860 * * `value`: local variable which will refer to each item in the `array` or each property value
36861 * of `object` during iteration.
36862 * * `key`: local variable which will refer to a property name in `object` during iteration.
36863 * * `label`: The result of this expression will be the label for `<option>` element. The
36864 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
36865 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
36866 * element. If not specified, `select` expression will default to `value`.
36867 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
36869 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
36870 * element. Return `true` to disable.
36871 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
36872 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
36873 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
36874 * even when the options are recreated (e.g. reloaded from the server).
36877 <example module="selectExample">
36878 <file name="index.html">
36880 angular.module('selectExample', [])
36881 .controller('ExampleController', ['$scope', function($scope) {
36883 {name:'black', shade:'dark'},
36884 {name:'white', shade:'light', notAnOption: true},
36885 {name:'red', shade:'dark'},
36886 {name:'blue', shade:'dark', notAnOption: true},
36887 {name:'yellow', shade:'light', notAnOption: false}
36889 $scope.myColor = $scope.colors[2]; // red
36892 <div ng-controller="ExampleController">
36894 <li ng-repeat="color in colors">
36895 <label>Name: <input ng-model="color.name"></label>
36896 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
36897 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
36900 <button ng-click="colors.push({})">add</button>
36904 <label>Color (null not allowed):
36905 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
36907 <label>Color (null allowed):
36908 <span class="nullable">
36909 <select ng-model="myColor" ng-options="color.name for color in colors">
36910 <option value="">-- choose color --</option>
36912 </span></label><br/>
36914 <label>Color grouped by shade:
36915 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
36919 <label>Color grouped by shade, with some disabled:
36920 <select ng-model="myColor"
36921 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
36927 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
36930 Currently selected: {{ {selected_color:myColor} }}
36931 <div style="border:solid 1px black; height:20px"
36932 ng-style="{'background-color':myColor.name}">
36936 <file name="protractor.js" type="protractor">
36937 it('should check ng-options', function() {
36938 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
36939 element.all(by.model('myColor')).first().click();
36940 element.all(by.css('select[ng-model="myColor"] option')).first().click();
36941 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
36942 element(by.css('.nullable select[ng-model="myColor"]')).click();
36943 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
36944 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
36950 // jshint maxlen: false
36951 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
36952 var 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]+?))?$/;
36953 // 1: value expression (valueFn)
36954 // 2: label expression (displayFn)
36955 // 3: group by expression (groupByFn)
36956 // 4: disable when expression (disableWhenFn)
36957 // 5: array item variable name
36958 // 6: object item key variable name
36959 // 7: object item value variable name
36960 // 8: collection expression
36961 // 9: track by expression
36962 // jshint maxlen: 100
36965 var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
36967 function parseOptionsExpression(optionsExp, selectElement, scope) {
36969 var match = optionsExp.match(NG_OPTIONS_REGEXP);
36971 throw ngOptionsMinErr('iexp',
36972 "Expected expression in form of " +
36973 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
36974 " but got '{0}'. Element: {1}",
36975 optionsExp, startingTag(selectElement));
36978 // Extract the parts from the ngOptions expression
36980 // The variable name for the value of the item in the collection
36981 var valueName = match[5] || match[7];
36982 // The variable name for the key of the item in the collection
36983 var keyName = match[6];
36985 // An expression that generates the viewValue for an option if there is a label expression
36986 var selectAs = / as /.test(match[0]) && match[1];
36987 // An expression that is used to track the id of each object in the options collection
36988 var trackBy = match[9];
36989 // An expression that generates the viewValue for an option if there is no label expression
36990 var valueFn = $parse(match[2] ? match[1] : valueName);
36991 var selectAsFn = selectAs && $parse(selectAs);
36992 var viewValueFn = selectAsFn || valueFn;
36993 var trackByFn = trackBy && $parse(trackBy);
36995 // Get the value by which we are going to track the option
36996 // if we have a trackFn then use that (passing scope and locals)
36997 // otherwise just hash the given viewValue
36998 var getTrackByValueFn = trackBy ?
36999 function(value, locals) { return trackByFn(scope, locals); } :
37000 function getHashOfValue(value) { return hashKey(value); };
37001 var getTrackByValue = function(value, key) {
37002 return getTrackByValueFn(value, getLocals(value, key));
37005 var displayFn = $parse(match[2] || match[1]);
37006 var groupByFn = $parse(match[3] || '');
37007 var disableWhenFn = $parse(match[4] || '');
37008 var valuesFn = $parse(match[8]);
37011 var getLocals = keyName ? function(value, key) {
37012 locals[keyName] = key;
37013 locals[valueName] = value;
37015 } : function(value) {
37016 locals[valueName] = value;
37021 function Option(selectValue, viewValue, label, group, disabled) {
37022 this.selectValue = selectValue;
37023 this.viewValue = viewValue;
37024 this.label = label;
37025 this.group = group;
37026 this.disabled = disabled;
37029 function getOptionValuesKeys(optionValues) {
37030 var optionValuesKeys;
37032 if (!keyName && isArrayLike(optionValues)) {
37033 optionValuesKeys = optionValues;
37035 // if object, extract keys, in enumeration order, unsorted
37036 optionValuesKeys = [];
37037 for (var itemKey in optionValues) {
37038 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
37039 optionValuesKeys.push(itemKey);
37043 return optionValuesKeys;
37048 getTrackByValue: getTrackByValue,
37049 getWatchables: $parse(valuesFn, function(optionValues) {
37050 // Create a collection of things that we would like to watch (watchedArray)
37051 // so that they can all be watched using a single $watchCollection
37052 // that only runs the handler once if anything changes
37053 var watchedArray = [];
37054 optionValues = optionValues || [];
37056 var optionValuesKeys = getOptionValuesKeys(optionValues);
37057 var optionValuesLength = optionValuesKeys.length;
37058 for (var index = 0; index < optionValuesLength; index++) {
37059 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
37060 var value = optionValues[key];
37062 var locals = getLocals(value, key);
37063 var selectValue = getTrackByValueFn(value, locals);
37064 watchedArray.push(selectValue);
37066 // Only need to watch the displayFn if there is a specific label expression
37067 if (match[2] || match[1]) {
37068 var label = displayFn(scope, locals);
37069 watchedArray.push(label);
37072 // Only need to watch the disableWhenFn if there is a specific disable expression
37074 var disableWhen = disableWhenFn(scope, locals);
37075 watchedArray.push(disableWhen);
37078 return watchedArray;
37081 getOptions: function() {
37083 var optionItems = [];
37084 var selectValueMap = {};
37086 // The option values were already computed in the `getWatchables` fn,
37087 // which must have been called to trigger `getOptions`
37088 var optionValues = valuesFn(scope) || [];
37089 var optionValuesKeys = getOptionValuesKeys(optionValues);
37090 var optionValuesLength = optionValuesKeys.length;
37092 for (var index = 0; index < optionValuesLength; index++) {
37093 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
37094 var value = optionValues[key];
37095 var locals = getLocals(value, key);
37096 var viewValue = viewValueFn(scope, locals);
37097 var selectValue = getTrackByValueFn(viewValue, locals);
37098 var label = displayFn(scope, locals);
37099 var group = groupByFn(scope, locals);
37100 var disabled = disableWhenFn(scope, locals);
37101 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
37103 optionItems.push(optionItem);
37104 selectValueMap[selectValue] = optionItem;
37108 items: optionItems,
37109 selectValueMap: selectValueMap,
37110 getOptionFromViewValue: function(value) {
37111 return selectValueMap[getTrackByValue(value)];
37113 getViewValueFromOption: function(option) {
37114 // If the viewValue could be an object that may be mutated by the application,
37115 // we need to make a copy and not return the reference to the value on the option.
37116 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
37124 // we can't just jqLite('<option>') since jqLite is not smart enough
37125 // to create it in <select> and IE barfs otherwise.
37126 var optionTemplate = window.document.createElement('option'),
37127 optGroupTemplate = window.document.createElement('optgroup');
37129 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
37131 var selectCtrl = ctrls[0];
37132 var ngModelCtrl = ctrls[1];
37133 var multiple = attr.multiple;
37135 // The emptyOption allows the application developer to provide their own custom "empty"
37136 // option when the viewValue does not match any of the option values.
37138 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
37139 if (children[i].value === '') {
37140 emptyOption = children.eq(i);
37145 var providedEmptyOption = !!emptyOption;
37147 var unknownOption = jqLite(optionTemplate.cloneNode(false));
37148 unknownOption.val('?');
37151 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
37152 // This stores the newly created options before they are appended to the select.
37153 // Since the contents are removed from the fragment when it is appended,
37154 // we only need to create it once.
37155 var listFragment = $document[0].createDocumentFragment();
37157 var renderEmptyOption = function() {
37158 if (!providedEmptyOption) {
37159 selectElement.prepend(emptyOption);
37161 selectElement.val('');
37162 emptyOption.prop('selected', true); // needed for IE
37163 emptyOption.attr('selected', true);
37166 var removeEmptyOption = function() {
37167 if (!providedEmptyOption) {
37168 emptyOption.remove();
37173 var renderUnknownOption = function() {
37174 selectElement.prepend(unknownOption);
37175 selectElement.val('?');
37176 unknownOption.prop('selected', true); // needed for IE
37177 unknownOption.attr('selected', true);
37180 var removeUnknownOption = function() {
37181 unknownOption.remove();
37184 // Update the controller methods for multiple selectable options
37187 selectCtrl.writeValue = function writeNgOptionsValue(value) {
37188 var option = options.getOptionFromViewValue(value);
37191 // Don't update the option when it is already selected.
37192 // For example, the browser will select the first option by default. In that case,
37193 // most properties are set automatically - except the `selected` attribute, which we
37196 if (selectElement[0].value !== option.selectValue) {
37197 removeUnknownOption();
37198 removeEmptyOption();
37200 selectElement[0].value = option.selectValue;
37201 option.element.selected = true;
37204 option.element.setAttribute('selected', 'selected');
37206 if (value === null || providedEmptyOption) {
37207 removeUnknownOption();
37208 renderEmptyOption();
37210 removeEmptyOption();
37211 renderUnknownOption();
37216 selectCtrl.readValue = function readNgOptionsValue() {
37218 var selectedOption = options.selectValueMap[selectElement.val()];
37220 if (selectedOption && !selectedOption.disabled) {
37221 removeEmptyOption();
37222 removeUnknownOption();
37223 return options.getViewValueFromOption(selectedOption);
37228 // If we are using `track by` then we must watch the tracked value on the model
37229 // since ngModel only watches for object identity change
37230 if (ngOptions.trackBy) {
37232 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
37233 function() { ngModelCtrl.$render(); }
37239 ngModelCtrl.$isEmpty = function(value) {
37240 return !value || value.length === 0;
37244 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
37245 options.items.forEach(function(option) {
37246 option.element.selected = false;
37250 value.forEach(function(item) {
37251 var option = options.getOptionFromViewValue(item);
37252 if (option) option.element.selected = true;
37258 selectCtrl.readValue = function readNgOptionsMultiple() {
37259 var selectedValues = selectElement.val() || [],
37262 forEach(selectedValues, function(value) {
37263 var option = options.selectValueMap[value];
37264 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
37270 // If we are using `track by` then we must watch these tracked values on the model
37271 // since ngModel only watches for object identity change
37272 if (ngOptions.trackBy) {
37274 scope.$watchCollection(function() {
37275 if (isArray(ngModelCtrl.$viewValue)) {
37276 return ngModelCtrl.$viewValue.map(function(value) {
37277 return ngOptions.getTrackByValue(value);
37281 ngModelCtrl.$render();
37288 if (providedEmptyOption) {
37290 // we need to remove it before calling selectElement.empty() because otherwise IE will
37291 // remove the label from the element. wtf?
37292 emptyOption.remove();
37294 // compile the element since there might be bindings in it
37295 $compile(emptyOption)(scope);
37297 // remove the class, which is added automatically because we recompile the element and it
37298 // becomes the compilation root
37299 emptyOption.removeClass('ng-scope');
37301 emptyOption = jqLite(optionTemplate.cloneNode(false));
37304 selectElement.empty();
37306 // We need to do this here to ensure that the options object is defined
37307 // when we first hit it in writeNgOptionsValue
37310 // We will re-render the option elements if the option values or labels change
37311 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
37313 // ------------------------------------------------------------------ //
37315 function addOptionElement(option, parent) {
37316 var optionElement = optionTemplate.cloneNode(false);
37317 parent.appendChild(optionElement);
37318 updateOptionElement(option, optionElement);
37322 function updateOptionElement(option, element) {
37323 option.element = element;
37324 element.disabled = option.disabled;
37325 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
37326 // selects in certain circumstances when multiple selects are next to each other and display
37327 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
37328 // See https://github.com/angular/angular.js/issues/11314 for more info.
37329 // This is unfortunately untestable with unit / e2e tests
37330 if (option.label !== element.label) {
37331 element.label = option.label;
37332 element.textContent = option.label;
37334 if (option.value !== element.value) element.value = option.selectValue;
37337 function updateOptions() {
37338 var previousValue = options && selectCtrl.readValue();
37340 // We must remove all current options, but cannot simply set innerHTML = null
37341 // since the providedEmptyOption might have an ngIf on it that inserts comments which we
37343 // Instead, iterate over the current option elements and remove them or their optgroup
37347 for (var i = options.items.length - 1; i >= 0; i--) {
37348 var option = options.items[i];
37349 if (option.group) {
37350 jqLiteRemove(option.element.parentNode);
37352 jqLiteRemove(option.element);
37357 options = ngOptions.getOptions();
37359 var groupElementMap = {};
37361 // Ensure that the empty option is always there if it was explicitly provided
37362 if (providedEmptyOption) {
37363 selectElement.prepend(emptyOption);
37366 options.items.forEach(function addOption(option) {
37369 if (isDefined(option.group)) {
37371 // This option is to live in a group
37372 // See if we have already created this group
37373 groupElement = groupElementMap[option.group];
37375 if (!groupElement) {
37377 groupElement = optGroupTemplate.cloneNode(false);
37378 listFragment.appendChild(groupElement);
37380 // Update the label on the group element
37381 groupElement.label = option.group;
37383 // Store it for use later
37384 groupElementMap[option.group] = groupElement;
37387 addOptionElement(option, groupElement);
37391 // This option is not in a group
37392 addOptionElement(option, listFragment);
37396 selectElement[0].appendChild(listFragment);
37398 ngModelCtrl.$render();
37400 // Check to see if the value has changed due to the update to the options
37401 if (!ngModelCtrl.$isEmpty(previousValue)) {
37402 var nextValue = selectCtrl.readValue();
37403 var isNotPrimitive = ngOptions.trackBy || multiple;
37404 if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
37405 ngModelCtrl.$setViewValue(nextValue);
37406 ngModelCtrl.$render();
37416 require: ['select', 'ngModel'],
37418 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
37419 // Deactivate the SelectController.register method to prevent
37420 // option directives from accidentally registering themselves
37421 // (and unwanted $destroy handlers etc.)
37422 ctrls[0].registerOption = noop;
37424 post: ngOptionsPostLink
37431 * @name ngPluralize
37435 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
37436 * These rules are bundled with angular.js, but can be overridden
37437 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
37438 * by specifying the mappings between
37439 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
37440 * and the strings to be displayed.
37442 * # Plural categories and explicit number rules
37444 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
37445 * in Angular's default en-US locale: "one" and "other".
37447 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
37448 * any number that is not 1), an explicit number rule can only match one number. For example, the
37449 * explicit number rule for "3" matches the number 3. There are examples of plural categories
37450 * and explicit number rules throughout the rest of this documentation.
37452 * # Configuring ngPluralize
37453 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
37454 * You can also provide an optional attribute, `offset`.
37456 * The value of the `count` attribute can be either a string or an {@link guide/expression
37457 * Angular expression}; these are evaluated on the current scope for its bound value.
37459 * The `when` attribute specifies the mappings between plural categories and the actual
37460 * string to be displayed. The value of the attribute should be a JSON object.
37462 * The following example shows how to configure ngPluralize:
37465 * <ng-pluralize count="personCount"
37466 when="{'0': 'Nobody is viewing.',
37467 * 'one': '1 person is viewing.',
37468 * 'other': '{} people are viewing.'}">
37472 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
37473 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
37474 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
37475 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
37476 * show "a dozen people are viewing".
37478 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
37479 * into pluralized strings. In the previous example, Angular will replace `{}` with
37480 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
37481 * for <span ng-non-bindable>{{numberExpression}}</span>.
37483 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
37484 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
37486 * # Configuring ngPluralize with offset
37487 * The `offset` attribute allows further customization of pluralized text, which can result in
37488 * a better user experience. For example, instead of the message "4 people are viewing this document",
37489 * you might display "John, Kate and 2 others are viewing this document".
37490 * The offset attribute allows you to offset a number by any desired value.
37491 * Let's take a look at an example:
37494 * <ng-pluralize count="personCount" offset=2
37495 * when="{'0': 'Nobody is viewing.',
37496 * '1': '{{person1}} is viewing.',
37497 * '2': '{{person1}} and {{person2}} are viewing.',
37498 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
37499 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
37503 * Notice that we are still using two plural categories(one, other), but we added
37504 * three explicit number rules 0, 1 and 2.
37505 * When one person, perhaps John, views the document, "John is viewing" will be shown.
37506 * When three people view the document, no explicit number rule is found, so
37507 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
37508 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
37511 * Note that when you specify offsets, you must provide explicit number rules for
37512 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
37513 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
37514 * plural categories "one" and "other".
37516 * @param {string|expression} count The variable to be bound to.
37517 * @param {string} when The mapping between plural category to its corresponding strings.
37518 * @param {number=} offset Offset to deduct from the total number.
37521 <example module="pluralizeExample">
37522 <file name="index.html">
37524 angular.module('pluralizeExample', [])
37525 .controller('ExampleController', ['$scope', function($scope) {
37526 $scope.person1 = 'Igor';
37527 $scope.person2 = 'Misko';
37528 $scope.personCount = 1;
37531 <div ng-controller="ExampleController">
37532 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
37533 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
37534 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
37536 <!--- Example with simple pluralization rules for en locale --->
37538 <ng-pluralize count="personCount"
37539 when="{'0': 'Nobody is viewing.',
37540 'one': '1 person is viewing.',
37541 'other': '{} people are viewing.'}">
37542 </ng-pluralize><br>
37544 <!--- Example with offset --->
37546 <ng-pluralize count="personCount" offset=2
37547 when="{'0': 'Nobody is viewing.',
37548 '1': '{{person1}} is viewing.',
37549 '2': '{{person1}} and {{person2}} are viewing.',
37550 'one': '{{person1}}, {{person2}} and one other person are viewing.',
37551 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
37555 <file name="protractor.js" type="protractor">
37556 it('should show correct pluralized string', function() {
37557 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
37558 var withOffset = element.all(by.css('ng-pluralize')).get(1);
37559 var countInput = element(by.model('personCount'));
37561 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
37562 expect(withOffset.getText()).toEqual('Igor is viewing.');
37564 countInput.clear();
37565 countInput.sendKeys('0');
37567 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
37568 expect(withOffset.getText()).toEqual('Nobody is viewing.');
37570 countInput.clear();
37571 countInput.sendKeys('2');
37573 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
37574 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
37576 countInput.clear();
37577 countInput.sendKeys('3');
37579 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
37580 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
37582 countInput.clear();
37583 countInput.sendKeys('4');
37585 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
37586 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
37588 it('should show data-bound names', function() {
37589 var withOffset = element.all(by.css('ng-pluralize')).get(1);
37590 var personCount = element(by.model('personCount'));
37591 var person1 = element(by.model('person1'));
37592 var person2 = element(by.model('person2'));
37593 personCount.clear();
37594 personCount.sendKeys('4');
37596 person1.sendKeys('Di');
37598 person2.sendKeys('Vojta');
37599 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
37604 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
37606 IS_WHEN = /^when(Minus)?(.+)$/;
37609 link: function(scope, element, attr) {
37610 var numberExp = attr.count,
37611 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
37612 offset = attr.offset || 0,
37613 whens = scope.$eval(whenExp) || {},
37615 startSymbol = $interpolate.startSymbol(),
37616 endSymbol = $interpolate.endSymbol(),
37617 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
37618 watchRemover = angular.noop,
37621 forEach(attr, function(expression, attributeName) {
37622 var tmpMatch = IS_WHEN.exec(attributeName);
37624 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
37625 whens[whenKey] = element.attr(attr.$attr[attributeName]);
37628 forEach(whens, function(expression, key) {
37629 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
37633 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
37634 var count = parseFloat(newVal);
37635 var countIsNaN = isNaN(count);
37637 if (!countIsNaN && !(count in whens)) {
37638 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
37639 // Otherwise, check it against pluralization rules in $locale service.
37640 count = $locale.pluralCat(count - offset);
37643 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
37644 // In JS `NaN !== NaN`, so we have to explicitly check.
37645 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
37647 var whenExpFn = whensExpFns[count];
37648 if (isUndefined(whenExpFn)) {
37649 if (newVal != null) {
37650 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
37652 watchRemover = noop;
37653 updateElementText();
37655 watchRemover = scope.$watch(whenExpFn, updateElementText);
37661 function updateElementText(newText) {
37662 element.text(newText || '');
37674 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
37675 * instance gets its own scope, where the given loop variable is set to the current collection item,
37676 * and `$index` is set to the item index or key.
37678 * Special properties are exposed on the local scope of each template instance, including:
37680 * | Variable | Type | Details |
37681 * |-----------|-----------------|-----------------------------------------------------------------------------|
37682 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
37683 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
37684 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
37685 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
37686 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
37687 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
37689 * <div class="alert alert-info">
37690 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
37691 * This may be useful when, for instance, nesting ngRepeats.
37695 * # Iterating over object properties
37697 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
37701 * <div ng-repeat="(key, value) in myObj"> ... </div>
37704 * However, there are a limitations compared to array iteration:
37706 * - The JavaScript specification does not define the order of keys
37707 * returned for an object, so Angular relies on the order returned by the browser
37708 * when running `for key in myObj`. Browsers generally follow the strategy of providing
37709 * keys in the order in which they were defined, although there are exceptions when keys are deleted
37710 * and reinstated. See the
37711 * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
37713 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
37714 * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
37716 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
37717 * objects, and will throw if used with one.
37719 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
37720 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
37721 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
37722 * or implement a `$watch` on the object yourself.
37725 * # Tracking and Duplicates
37727 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
37728 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
37730 * * When an item is added, a new instance of the template is added to the DOM.
37731 * * When an item is removed, its template instance is removed from the DOM.
37732 * * When items are reordered, their respective templates are reordered in the DOM.
37734 * To minimize creation of DOM elements, `ngRepeat` uses a function
37735 * to "keep track" of all items in the collection and their corresponding DOM elements.
37736 * For example, if an item is added to the collection, ngRepeat will know that all other items
37737 * already have DOM elements, and will not re-render them.
37739 * The default tracking function (which tracks items by their identity) does not allow
37740 * duplicate items in arrays. This is because when there are duplicates, it is not possible
37741 * to maintain a one-to-one mapping between collection items and DOM elements.
37743 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
37744 * with your own using the `track by` expression.
37746 * For example, you may track items by the index of each item in the collection, using the
37747 * special scope property `$index`:
37749 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
37754 * You may also use arbitrary expressions in `track by`, including references to custom functions
37757 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
37762 * <div class="alert alert-success">
37763 * If you are working with objects that have an identifier property, you should track
37764 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
37765 * will not have to rebuild the DOM elements for items it has already rendered, even if the
37766 * JavaScript objects in the collection have been substituted for new ones. For large collections,
37767 * this significantly improves rendering performance. If you don't have a unique identifier,
37768 * `track by $index` can also provide a performance boost.
37771 * <div ng-repeat="model in collection track by model.id">
37776 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
37777 * `$id` function, which tracks items by their identity:
37779 * <div ng-repeat="obj in collection track by $id(obj)">
37784 * <div class="alert alert-warning">
37785 * **Note:** `track by` must always be the last expression:
37788 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
37793 * # Special repeat start and end points
37794 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
37795 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
37796 * 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)
37797 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
37799 * The example below makes use of this feature:
37801 * <header ng-repeat-start="item in items">
37802 * Header {{ item }}
37804 * <div class="body">
37807 * <footer ng-repeat-end>
37808 * Footer {{ item }}
37812 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
37817 * <div class="body">
37826 * <div class="body">
37834 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
37835 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
37838 * | Animation | Occurs |
37839 * |----------------------------------|-------------------------------------|
37840 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
37841 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
37842 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
37844 * See the example below for defining CSS animations with ngRepeat.
37849 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
37850 * formats are currently supported:
37852 * * `variable in expression` – where variable is the user defined loop variable and `expression`
37853 * is a scope expression giving the collection to enumerate.
37855 * For example: `album in artist.albums`.
37857 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
37858 * and `expression` is the scope expression giving the collection to enumerate.
37860 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
37862 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
37863 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
37864 * is specified, ng-repeat associates elements by identity. It is an error to have
37865 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
37866 * mapped to the same DOM element, which is not possible.)
37868 * Note that the tracking expression must come last, after any filters, and the alias expression.
37870 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
37871 * will be associated by item identity in the array.
37873 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
37874 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
37875 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
37876 * element in the same way in the DOM.
37878 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
37879 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
37880 * property is same.
37882 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
37883 * to items in conjunction with a tracking expression.
37885 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
37886 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
37887 * when a filter is active on the repeater, but the filtered result set is empty.
37889 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
37890 * the items have been processed through the filter.
37892 * 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
37893 * (and not as operator, inside an expression).
37895 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
37898 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
37899 * results by name. New (entering) and removed (leaving) items are animated.
37900 <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
37901 <file name="index.html">
37902 <div ng-controller="repeatController">
37903 I have {{friends.length}} friends. They are:
37904 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
37905 <ul class="example-animate-container">
37906 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
37907 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
37909 <li class="animate-repeat" ng-if="results.length == 0">
37910 <strong>No results found...</strong>
37915 <file name="script.js">
37916 angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
37918 {name:'John', age:25, gender:'boy'},
37919 {name:'Jessie', age:30, gender:'girl'},
37920 {name:'Johanna', age:28, gender:'girl'},
37921 {name:'Joy', age:15, gender:'girl'},
37922 {name:'Mary', age:28, gender:'girl'},
37923 {name:'Peter', age:95, gender:'boy'},
37924 {name:'Sebastian', age:50, gender:'boy'},
37925 {name:'Erika', age:27, gender:'girl'},
37926 {name:'Patrick', age:40, gender:'boy'},
37927 {name:'Samantha', age:60, gender:'girl'}
37931 <file name="animations.css">
37932 .example-animate-container {
37934 border:1px solid black;
37943 box-sizing:border-box;
37946 .animate-repeat.ng-move,
37947 .animate-repeat.ng-enter,
37948 .animate-repeat.ng-leave {
37949 transition:all linear 0.5s;
37952 .animate-repeat.ng-leave.ng-leave-active,
37953 .animate-repeat.ng-move,
37954 .animate-repeat.ng-enter {
37959 .animate-repeat.ng-leave,
37960 .animate-repeat.ng-move.ng-move-active,
37961 .animate-repeat.ng-enter.ng-enter-active {
37966 <file name="protractor.js" type="protractor">
37967 var friends = element.all(by.repeater('friend in friends'));
37969 it('should render initial data set', function() {
37970 expect(friends.count()).toBe(10);
37971 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
37972 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
37973 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
37974 expect(element(by.binding('friends.length')).getText())
37975 .toMatch("I have 10 friends. They are:");
37978 it('should update repeater when filter predicate changes', function() {
37979 expect(friends.count()).toBe(10);
37981 element(by.model('q')).sendKeys('ma');
37983 expect(friends.count()).toBe(2);
37984 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
37985 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
37990 var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
37991 var NG_REMOVED = '$$NG_REMOVED';
37992 var ngRepeatMinErr = minErr('ngRepeat');
37994 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
37995 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
37996 scope[valueIdentifier] = value;
37997 if (keyIdentifier) scope[keyIdentifier] = key;
37998 scope.$index = index;
37999 scope.$first = (index === 0);
38000 scope.$last = (index === (arrayLength - 1));
38001 scope.$middle = !(scope.$first || scope.$last);
38002 // jshint bitwise: false
38003 scope.$odd = !(scope.$even = (index&1) === 0);
38004 // jshint bitwise: true
38007 var getBlockStart = function(block) {
38008 return block.clone[0];
38011 var getBlockEnd = function(block) {
38012 return block.clone[block.clone.length - 1];
38018 multiElement: true,
38019 transclude: 'element',
38023 compile: function ngRepeatCompile($element, $attr) {
38024 var expression = $attr.ngRepeat;
38025 var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
38027 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*$/);
38030 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
38034 var lhs = match[1];
38035 var rhs = match[2];
38036 var aliasAs = match[3];
38037 var trackByExp = match[4];
38039 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
38042 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
38045 var valueIdentifier = match[3] || match[1];
38046 var keyIdentifier = match[2];
38048 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
38049 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
38050 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
38054 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
38055 var hashFnLocals = {$id: hashKey};
38058 trackByExpGetter = $parse(trackByExp);
38060 trackByIdArrayFn = function(key, value) {
38061 return hashKey(value);
38063 trackByIdObjFn = function(key) {
38068 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
38070 if (trackByExpGetter) {
38071 trackByIdExpFn = function(key, value, index) {
38072 // assign key, value, and $index to the locals so that they can be used in hash functions
38073 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
38074 hashFnLocals[valueIdentifier] = value;
38075 hashFnLocals.$index = index;
38076 return trackByExpGetter($scope, hashFnLocals);
38080 // Store a list of elements from previous run. This is a hash where key is the item from the
38081 // iterator, and the value is objects with following properties.
38082 // - scope: bound scope
38083 // - element: previous element.
38084 // - index: position
38086 // We are using no-proto object so that we don't need to guard against inherited props via
38088 var lastBlockMap = createMap();
38091 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
38093 previousNode = $element[0], // node that cloned nodes should be inserted after
38094 // initialized to the comment node anchor
38096 // Same as lastBlockMap but it has the current state. It will become the
38097 // lastBlockMap on the next iteration.
38098 nextBlockMap = createMap(),
38100 key, value, // key/value of iteration
38104 block, // last object information {scope, element, id}
38109 $scope[aliasAs] = collection;
38112 if (isArrayLike(collection)) {
38113 collectionKeys = collection;
38114 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
38116 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
38117 // if object, extract keys, in enumeration order, unsorted
38118 collectionKeys = [];
38119 for (var itemKey in collection) {
38120 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
38121 collectionKeys.push(itemKey);
38126 collectionLength = collectionKeys.length;
38127 nextBlockOrder = new Array(collectionLength);
38129 // locate existing items
38130 for (index = 0; index < collectionLength; index++) {
38131 key = (collection === collectionKeys) ? index : collectionKeys[index];
38132 value = collection[key];
38133 trackById = trackByIdFn(key, value, index);
38134 if (lastBlockMap[trackById]) {
38135 // found previously seen block
38136 block = lastBlockMap[trackById];
38137 delete lastBlockMap[trackById];
38138 nextBlockMap[trackById] = block;
38139 nextBlockOrder[index] = block;
38140 } else if (nextBlockMap[trackById]) {
38141 // if collision detected. restore lastBlockMap and throw an error
38142 forEach(nextBlockOrder, function(block) {
38143 if (block && block.scope) lastBlockMap[block.id] = block;
38145 throw ngRepeatMinErr('dupes',
38146 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
38147 expression, trackById, value);
38149 // new never before seen block
38150 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
38151 nextBlockMap[trackById] = true;
38155 // remove leftover items
38156 for (var blockKey in lastBlockMap) {
38157 block = lastBlockMap[blockKey];
38158 elementsToRemove = getBlockNodes(block.clone);
38159 $animate.leave(elementsToRemove);
38160 if (elementsToRemove[0].parentNode) {
38161 // if the element was not removed yet because of pending animation, mark it as deleted
38162 // so that we can ignore it later
38163 for (index = 0, length = elementsToRemove.length; index < length; index++) {
38164 elementsToRemove[index][NG_REMOVED] = true;
38167 block.scope.$destroy();
38170 // we are not using forEach for perf reasons (trying to avoid #call)
38171 for (index = 0; index < collectionLength; index++) {
38172 key = (collection === collectionKeys) ? index : collectionKeys[index];
38173 value = collection[key];
38174 block = nextBlockOrder[index];
38177 // if we have already seen this object, then we need to reuse the
38178 // associated scope/element
38180 nextNode = previousNode;
38182 // skip nodes that are already pending removal via leave animation
38184 nextNode = nextNode.nextSibling;
38185 } while (nextNode && nextNode[NG_REMOVED]);
38187 if (getBlockStart(block) != nextNode) {
38188 // existing item which got moved
38189 $animate.move(getBlockNodes(block.clone), null, previousNode);
38191 previousNode = getBlockEnd(block);
38192 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
38194 // new item which we don't know about
38195 $transclude(function ngRepeatTransclude(clone, scope) {
38196 block.scope = scope;
38197 // http://jsperf.com/clone-vs-createcomment
38198 var endNode = ngRepeatEndComment.cloneNode(false);
38199 clone[clone.length++] = endNode;
38201 $animate.enter(clone, null, previousNode);
38202 previousNode = endNode;
38203 // Note: We only need the first/last node of the cloned nodes.
38204 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
38205 // by a directive with templateUrl when its template arrives.
38206 block.clone = clone;
38207 nextBlockMap[block.id] = block;
38208 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
38212 lastBlockMap = nextBlockMap;
38219 var NG_HIDE_CLASS = 'ng-hide';
38220 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
38227 * The `ngShow` directive shows or hides the given HTML element based on the expression
38228 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
38229 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
38230 * in AngularJS and sets the display style to none (using an !important flag).
38231 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
38234 * <!-- when $scope.myValue is truthy (element is visible) -->
38235 * <div ng-show="myValue"></div>
38237 * <!-- when $scope.myValue is falsy (element is hidden) -->
38238 * <div ng-show="myValue" class="ng-hide"></div>
38241 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
38242 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
38243 * from the element causing the element not to appear hidden.
38245 * ## Why is !important used?
38247 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
38248 * can be easily overridden by heavier selectors. For example, something as simple
38249 * as changing the display style on a HTML list item would make hidden elements appear visible.
38250 * This also becomes a bigger issue when dealing with CSS frameworks.
38252 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
38253 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
38254 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
38256 * ### Overriding `.ng-hide`
38258 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
38259 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
38260 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
38261 * with extra animation classes that can be added.
38264 * .ng-hide:not(.ng-hide-animate) {
38265 * /* this is just another form of hiding an element */
38266 * display: block!important;
38267 * position: absolute;
38273 * By default you don't need to override in CSS anything and the animations will work around the display style.
38275 * ## A note about animations with `ngShow`
38277 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
38278 * is true and false. This system works like the animation system present with ngClass except that
38279 * you must also include the !important flag to override the display property
38280 * so that you can perform an animation when the element is hidden during the time of the animation.
38284 * //a working example can be found at the bottom of this page
38286 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
38287 * /* this is required as of 1.3x to properly
38288 * apply all styling in a show/hide animation */
38289 * transition: 0s linear all;
38292 * .my-element.ng-hide-add-active,
38293 * .my-element.ng-hide-remove-active {
38294 * /* the transition is defined in the active class */
38295 * transition: 1s linear all;
38298 * .my-element.ng-hide-add { ... }
38299 * .my-element.ng-hide-add.ng-hide-add-active { ... }
38300 * .my-element.ng-hide-remove { ... }
38301 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
38304 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
38305 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
38308 * | Animation | Occurs |
38309 * |----------------------------------|-------------------------------------|
38310 * | {@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 |
38311 * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
38314 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
38315 * then the element is shown or hidden respectively.
38318 <example module="ngAnimate" deps="angular-animate.js" animations="true">
38319 <file name="index.html">
38320 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
38323 <div class="check-element animate-show" ng-show="checked">
38324 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
38329 <div class="check-element animate-show" ng-hide="checked">
38330 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
38334 <file name="glyphicons.css">
38335 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
38337 <file name="animations.css">
38342 border: 1px solid black;
38346 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
38347 transition: all linear 0.5s;
38350 .animate-show.ng-hide {
38358 border: 1px solid black;
38362 <file name="protractor.js" type="protractor">
38363 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
38364 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
38366 it('should check ng-show / ng-hide', function() {
38367 expect(thumbsUp.isDisplayed()).toBeFalsy();
38368 expect(thumbsDown.isDisplayed()).toBeTruthy();
38370 element(by.model('checked')).click();
38372 expect(thumbsUp.isDisplayed()).toBeTruthy();
38373 expect(thumbsDown.isDisplayed()).toBeFalsy();
38378 var ngShowDirective = ['$animate', function($animate) {
38381 multiElement: true,
38382 link: function(scope, element, attr) {
38383 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
38384 // we're adding a temporary, animation-specific class for ng-hide since this way
38385 // we can control when the element is actually displayed on screen without having
38386 // to have a global/greedy CSS selector that breaks when other animations are run.
38387 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
38388 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
38389 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
38403 * The `ngHide` directive shows or hides the given HTML element based on the expression
38404 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
38405 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
38406 * in AngularJS and sets the display style to none (using an !important flag).
38407 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
38410 * <!-- when $scope.myValue is truthy (element is hidden) -->
38411 * <div ng-hide="myValue" class="ng-hide"></div>
38413 * <!-- when $scope.myValue is falsy (element is visible) -->
38414 * <div ng-hide="myValue"></div>
38417 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
38418 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
38419 * from the element causing the element not to appear hidden.
38421 * ## Why is !important used?
38423 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
38424 * can be easily overridden by heavier selectors. For example, something as simple
38425 * as changing the display style on a HTML list item would make hidden elements appear visible.
38426 * This also becomes a bigger issue when dealing with CSS frameworks.
38428 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
38429 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
38430 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
38432 * ### Overriding `.ng-hide`
38434 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
38435 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
38440 * /* this is just another form of hiding an element */
38441 * display: block!important;
38442 * position: absolute;
38448 * By default you don't need to override in CSS anything and the animations will work around the display style.
38450 * ## A note about animations with `ngHide`
38452 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
38453 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
38454 * CSS class is added and removed for you instead of your own CSS class.
38458 * //a working example can be found at the bottom of this page
38460 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
38461 * transition: 0.5s linear all;
38464 * .my-element.ng-hide-add { ... }
38465 * .my-element.ng-hide-add.ng-hide-add-active { ... }
38466 * .my-element.ng-hide-remove { ... }
38467 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
38470 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
38471 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
38474 * | Animation | Occurs |
38475 * |----------------------------------|-------------------------------------|
38476 * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
38477 * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
38481 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
38482 * the element is shown or hidden respectively.
38485 <example module="ngAnimate" deps="angular-animate.js" animations="true">
38486 <file name="index.html">
38487 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
38490 <div class="check-element animate-hide" ng-show="checked">
38491 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
38496 <div class="check-element animate-hide" ng-hide="checked">
38497 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
38501 <file name="glyphicons.css">
38502 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
38504 <file name="animations.css">
38506 transition: all linear 0.5s;
38510 border: 1px solid black;
38514 .animate-hide.ng-hide {
38522 border: 1px solid black;
38526 <file name="protractor.js" type="protractor">
38527 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
38528 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
38530 it('should check ng-show / ng-hide', function() {
38531 expect(thumbsUp.isDisplayed()).toBeFalsy();
38532 expect(thumbsDown.isDisplayed()).toBeTruthy();
38534 element(by.model('checked')).click();
38536 expect(thumbsUp.isDisplayed()).toBeTruthy();
38537 expect(thumbsDown.isDisplayed()).toBeFalsy();
38542 var ngHideDirective = ['$animate', function($animate) {
38545 multiElement: true,
38546 link: function(scope, element, attr) {
38547 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
38548 // The comment inside of the ngShowDirective explains why we add and
38549 // remove a temporary class for the show/hide animation
38550 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
38551 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
38564 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
38567 * @param {expression} ngStyle
38569 * {@link guide/expression Expression} which evals to an
38570 * object whose keys are CSS style names and values are corresponding values for those CSS
38573 * Since some CSS style names are not valid keys for an object, they must be quoted.
38574 * See the 'background-color' style in the example below.
38578 <file name="index.html">
38579 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
38580 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
38581 <input type="button" value="clear" ng-click="myStyle={}">
38583 <span ng-style="myStyle">Sample Text</span>
38584 <pre>myStyle={{myStyle}}</pre>
38586 <file name="style.css">
38591 <file name="protractor.js" type="protractor">
38592 var colorSpan = element(by.css('span'));
38594 it('should check ng-style', function() {
38595 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
38596 element(by.css('input[value=\'set color\']')).click();
38597 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
38598 element(by.css('input[value=clear]')).click();
38599 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
38604 var ngStyleDirective = ngDirective(function(scope, element, attr) {
38605 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
38606 if (oldStyles && (newStyles !== oldStyles)) {
38607 forEach(oldStyles, function(val, style) { element.css(style, '');});
38609 if (newStyles) element.css(newStyles);
38619 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
38620 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
38621 * as specified in the template.
38623 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
38624 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
38625 * matches the value obtained from the evaluated expression. In other words, you define a container element
38626 * (where you place the directive), place an expression on the **`on="..."` attribute**
38627 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
38628 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
38629 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
38630 * attribute is displayed.
38632 * <div class="alert alert-info">
38633 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
38634 * as literal string values to match against.
38635 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
38636 * value of the expression `$scope.someVal`.
38640 * | Animation | Occurs |
38641 * |----------------------------------|-------------------------------------|
38642 * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
38643 * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
38648 * <ANY ng-switch="expression">
38649 * <ANY ng-switch-when="matchValue1">...</ANY>
38650 * <ANY ng-switch-when="matchValue2">...</ANY>
38651 * <ANY ng-switch-default>...</ANY>
38658 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
38659 * On child elements add:
38661 * * `ngSwitchWhen`: the case statement to match against. If match then this
38662 * case will be displayed. If the same match appears multiple times, all the
38663 * elements will be displayed.
38664 * * `ngSwitchDefault`: the default case when no other case match. If there
38665 * are multiple default cases, all of them will be displayed when no other
38670 <example module="switchExample" deps="angular-animate.js" animations="true">
38671 <file name="index.html">
38672 <div ng-controller="ExampleController">
38673 <select ng-model="selection" ng-options="item for item in items">
38675 <code>selection={{selection}}</code>
38677 <div class="animate-switch-container"
38678 ng-switch on="selection">
38679 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
38680 <div class="animate-switch" ng-switch-when="home">Home Span</div>
38681 <div class="animate-switch" ng-switch-default>default</div>
38685 <file name="script.js">
38686 angular.module('switchExample', ['ngAnimate'])
38687 .controller('ExampleController', ['$scope', function($scope) {
38688 $scope.items = ['settings', 'home', 'other'];
38689 $scope.selection = $scope.items[0];
38692 <file name="animations.css">
38693 .animate-switch-container {
38696 border:1px solid black;
38705 .animate-switch.ng-animate {
38706 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
38715 .animate-switch.ng-leave.ng-leave-active,
38716 .animate-switch.ng-enter {
38719 .animate-switch.ng-leave,
38720 .animate-switch.ng-enter.ng-enter-active {
38724 <file name="protractor.js" type="protractor">
38725 var switchElem = element(by.css('[ng-switch]'));
38726 var select = element(by.model('selection'));
38728 it('should start in settings', function() {
38729 expect(switchElem.getText()).toMatch(/Settings Div/);
38731 it('should change to home', function() {
38732 select.all(by.css('option')).get(1).click();
38733 expect(switchElem.getText()).toMatch(/Home Span/);
38735 it('should select default', function() {
38736 select.all(by.css('option')).get(2).click();
38737 expect(switchElem.getText()).toMatch(/default/);
38742 var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
38744 require: 'ngSwitch',
38746 // asks for $scope to fool the BC controller module
38747 controller: ['$scope', function ngSwitchController() {
38750 link: function(scope, element, attr, ngSwitchController) {
38751 var watchExpr = attr.ngSwitch || attr.on,
38752 selectedTranscludes = [],
38753 selectedElements = [],
38754 previousLeaveAnimations = [],
38755 selectedScopes = [];
38757 var spliceFactory = function(array, index) {
38758 return function() { array.splice(index, 1); };
38761 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
38763 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
38764 $animate.cancel(previousLeaveAnimations[i]);
38766 previousLeaveAnimations.length = 0;
38768 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
38769 var selected = getBlockNodes(selectedElements[i].clone);
38770 selectedScopes[i].$destroy();
38771 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
38772 promise.then(spliceFactory(previousLeaveAnimations, i));
38775 selectedElements.length = 0;
38776 selectedScopes.length = 0;
38778 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
38779 forEach(selectedTranscludes, function(selectedTransclude) {
38780 selectedTransclude.transclude(function(caseElement, selectedScope) {
38781 selectedScopes.push(selectedScope);
38782 var anchor = selectedTransclude.element;
38783 caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
38784 var block = { clone: caseElement };
38786 selectedElements.push(block);
38787 $animate.enter(caseElement, anchor.parent(), anchor);
38796 var ngSwitchWhenDirective = ngDirective({
38797 transclude: 'element',
38799 require: '^ngSwitch',
38800 multiElement: true,
38801 link: function(scope, element, attrs, ctrl, $transclude) {
38802 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
38803 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
38807 var ngSwitchDefaultDirective = ngDirective({
38808 transclude: 'element',
38810 require: '^ngSwitch',
38811 multiElement: true,
38812 link: function(scope, element, attr, ctrl, $transclude) {
38813 ctrl.cases['?'] = (ctrl.cases['?'] || []);
38814 ctrl.cases['?'].push({ transclude: $transclude, element: element });
38820 * @name ngTransclude
38824 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
38826 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
38827 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
38829 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
38830 * content of this element will be removed before the transcluded content is inserted.
38831 * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
38832 * that no transcluded content is provided.
38836 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
38837 * or its value is the same as the name of the attribute then the default slot is used.
38840 * ### Basic transclusion
38841 * This example demonstrates basic transclusion of content into a component directive.
38842 * <example name="simpleTranscludeExample" module="transcludeExample">
38843 * <file name="index.html">
38845 * angular.module('transcludeExample', [])
38846 * .directive('pane', function(){
38849 * transclude: true,
38850 * scope: { title:'@' },
38851 * template: '<div style="border: 1px solid black;">' +
38852 * '<div style="background-color: gray">{{title}}</div>' +
38853 * '<ng-transclude></ng-transclude>' +
38857 * .controller('ExampleController', ['$scope', function($scope) {
38858 * $scope.title = 'Lorem Ipsum';
38859 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
38862 * <div ng-controller="ExampleController">
38863 * <input ng-model="title" aria-label="title"> <br/>
38864 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
38865 * <pane title="{{title}}">{{text}}</pane>
38868 * <file name="protractor.js" type="protractor">
38869 * it('should have transcluded', function() {
38870 * var titleElement = element(by.model('title'));
38871 * titleElement.clear();
38872 * titleElement.sendKeys('TITLE');
38873 * var textElement = element(by.model('text'));
38874 * textElement.clear();
38875 * textElement.sendKeys('TEXT');
38876 * expect(element(by.binding('title')).getText()).toEqual('TITLE');
38877 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
38883 * ### Transclude fallback content
38884 * This example shows how to use `NgTransclude` with fallback content, that
38885 * is displayed if no transcluded content is provided.
38887 * <example module="transcludeFallbackContentExample">
38888 * <file name="index.html">
38890 * angular.module('transcludeFallbackContentExample', [])
38891 * .directive('myButton', function(){
38894 * transclude: true,
38896 * template: '<button style="cursor: pointer;">' +
38897 * '<ng-transclude>' +
38898 * '<b style="color: red;">Button1</b>' +
38899 * '</ng-transclude>' +
38904 * <!-- fallback button content -->
38905 * <my-button id="fallback"></my-button>
38906 * <!-- modified button content -->
38907 * <my-button id="modified">
38908 * <i style="color: green;">Button2</i>
38911 * <file name="protractor.js" type="protractor">
38912 * it('should have different transclude element content', function() {
38913 * expect(element(by.id('fallback')).getText()).toBe('Button1');
38914 * expect(element(by.id('modified')).getText()).toBe('Button2');
38920 * ### Multi-slot transclusion
38921 * This example demonstrates using multi-slot transclusion in a component directive.
38922 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
38923 * <file name="index.html">
38925 * .title, .footer {
38926 * background-color: gray
38929 * <div ng-controller="ExampleController">
38930 * <input ng-model="title" aria-label="title"> <br/>
38931 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
38933 * <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
38934 * <pane-body><p>{{text}}</p></pane-body>
38938 * <file name="app.js">
38939 * angular.module('multiSlotTranscludeExample', [])
38940 * .directive('pane', function(){
38944 * 'title': '?paneTitle',
38945 * 'body': 'paneBody',
38946 * 'footer': '?paneFooter'
38948 * template: '<div style="border: 1px solid black;">' +
38949 * '<div class="title" ng-transclude="title">Fallback Title</div>' +
38950 * '<div ng-transclude="body"></div>' +
38951 * '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
38955 * .controller('ExampleController', ['$scope', function($scope) {
38956 * $scope.title = 'Lorem Ipsum';
38957 * $scope.link = "https://google.com";
38958 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
38961 * <file name="protractor.js" type="protractor">
38962 * it('should have transcluded the title and the body', function() {
38963 * var titleElement = element(by.model('title'));
38964 * titleElement.clear();
38965 * titleElement.sendKeys('TITLE');
38966 * var textElement = element(by.model('text'));
38967 * textElement.clear();
38968 * textElement.sendKeys('TEXT');
38969 * expect(element(by.css('.title')).getText()).toEqual('TITLE');
38970 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
38971 * expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
38976 var ngTranscludeMinErr = minErr('ngTransclude');
38977 var ngTranscludeDirective = ngDirective({
38979 link: function($scope, $element, $attrs, controller, $transclude) {
38981 if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
38982 // If the attribute is of the form: `ng-transclude="ng-transclude"`
38983 // then treat it like the default
38984 $attrs.ngTransclude = '';
38987 function ngTranscludeCloneAttachFn(clone) {
38988 if (clone.length) {
38990 $element.append(clone);
38994 if (!$transclude) {
38995 throw ngTranscludeMinErr('orphan',
38996 'Illegal use of ngTransclude directive in the template! ' +
38997 'No parent directive that requires a transclusion found. ' +
38999 startingTag($element));
39002 // If there is no slot name defined or the slot name is not optional
39003 // then transclude the slot
39004 var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
39005 $transclude(ngTranscludeCloneAttachFn, null, slotName);
39015 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
39016 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
39017 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
39018 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
39019 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
39021 * @param {string} type Must be set to `'text/ng-template'`.
39022 * @param {string} id Cache name of the template.
39026 <file name="index.html">
39027 <script type="text/ng-template" id="/tpl.html">
39028 Content of the template.
39031 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
39032 <div id="tpl-content" ng-include src="currentTpl"></div>
39034 <file name="protractor.js" type="protractor">
39035 it('should load template defined inside script tag', function() {
39036 element(by.css('#tpl-link')).click();
39037 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
39042 var scriptDirective = ['$templateCache', function($templateCache) {
39046 compile: function(element, attr) {
39047 if (attr.type == 'text/ng-template') {
39048 var templateUrl = attr.id,
39049 text = element[0].text;
39051 $templateCache.put(templateUrl, text);
39057 var noopNgModelController = { $setViewValue: noop, $render: noop };
39059 function chromeHack(optionElement) {
39060 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
39061 // Adding an <option selected="selected"> element to a <select required="required"> should
39062 // automatically select the new element
39063 if (optionElement[0].hasAttribute('selected')) {
39064 optionElement[0].selected = true;
39070 * @name select.SelectController
39072 * The controller for the `<select>` directive. This provides support for reading
39073 * and writing the selected value(s) of the control and also coordinates dynamically
39074 * added `<option>` elements, perhaps by an `ngRepeat` directive.
39076 var SelectController =
39077 ['$element', '$scope', function($element, $scope) {
39080 optionsMap = new HashMap();
39082 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
39083 self.ngModelCtrl = noopNgModelController;
39085 // The "unknown" option is one that is prepended to the list if the viewValue
39086 // does not match any of the options. When it is rendered the value of the unknown
39087 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
39089 // We can't just jqLite('<option>') since jqLite is not smart enough
39090 // to create it in <select> and IE barfs otherwise.
39091 self.unknownOption = jqLite(window.document.createElement('option'));
39092 self.renderUnknownOption = function(val) {
39093 var unknownVal = '? ' + hashKey(val) + ' ?';
39094 self.unknownOption.val(unknownVal);
39095 $element.prepend(self.unknownOption);
39096 $element.val(unknownVal);
39099 $scope.$on('$destroy', function() {
39100 // disable unknown option so that we don't do work when the whole select is being destroyed
39101 self.renderUnknownOption = noop;
39104 self.removeUnknownOption = function() {
39105 if (self.unknownOption.parent()) self.unknownOption.remove();
39109 // Read the value of the select control, the implementation of this changes depending
39110 // upon whether the select can have multiple values and whether ngOptions is at work.
39111 self.readValue = function readSingleValue() {
39112 self.removeUnknownOption();
39113 return $element.val();
39117 // Write the value to the select control, the implementation of this changes depending
39118 // upon whether the select can have multiple values and whether ngOptions is at work.
39119 self.writeValue = function writeSingleValue(value) {
39120 if (self.hasOption(value)) {
39121 self.removeUnknownOption();
39122 $element.val(value);
39123 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
39125 if (value == null && self.emptyOption) {
39126 self.removeUnknownOption();
39129 self.renderUnknownOption(value);
39135 // Tell the select control that an option, with the given value, has been added
39136 self.addOption = function(value, element) {
39137 // Skip comment nodes, as they only pollute the `optionsMap`
39138 if (element[0].nodeType === NODE_TYPE_COMMENT) return;
39140 assertNotHasOwnProperty(value, '"option value"');
39141 if (value === '') {
39142 self.emptyOption = element;
39144 var count = optionsMap.get(value) || 0;
39145 optionsMap.put(value, count + 1);
39146 self.ngModelCtrl.$render();
39147 chromeHack(element);
39150 // Tell the select control that an option, with the given value, has been removed
39151 self.removeOption = function(value) {
39152 var count = optionsMap.get(value);
39155 optionsMap.remove(value);
39156 if (value === '') {
39157 self.emptyOption = undefined;
39160 optionsMap.put(value, count - 1);
39165 // Check whether the select control has an option matching the given value
39166 self.hasOption = function(value) {
39167 return !!optionsMap.get(value);
39171 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
39173 if (interpolateValueFn) {
39174 // The value attribute is interpolated
39176 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
39177 if (isDefined(oldVal)) {
39178 self.removeOption(oldVal);
39181 self.addOption(newVal, optionElement);
39183 } else if (interpolateTextFn) {
39184 // The text content is interpolated
39185 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
39186 optionAttrs.$set('value', newVal);
39187 if (oldVal !== newVal) {
39188 self.removeOption(oldVal);
39190 self.addOption(newVal, optionElement);
39193 // The value attribute is static
39194 self.addOption(optionAttrs.value, optionElement);
39197 optionElement.on('$destroy', function() {
39198 self.removeOption(optionAttrs.value);
39199 self.ngModelCtrl.$render();
39210 * HTML `SELECT` element with angular data-binding.
39212 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
39213 * between the scope and the `<select>` control (including setting default values).
39214 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
39215 * {@link ngOptions `ngOptions`} directives.
39217 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
39218 * to the model identified by the `ngModel` directive. With static or repeated options, this is
39219 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
39220 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
39222 * <div class="alert alert-warning">
39223 * Note that the value of a `select` directive used without `ngOptions` is always a string.
39224 * When the model needs to be bound to a non-string value, you must either explicitly convert it
39225 * using a directive (see example below) or use `ngOptions` to specify the set of options.
39226 * This is because an option element can only be bound to string values at present.
39229 * If the viewValue of `ngModel` does not match any of the options, then the control
39230 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
39232 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
39233 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
39234 * option. See example below for demonstration.
39236 * <div class="alert alert-info">
39237 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
39238 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
39239 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
39240 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
39241 * a new scope for each repeated instance.
39245 * @param {string} ngModel Assignable angular expression to data-bind to.
39246 * @param {string=} name Property name of the form under which the control is published.
39247 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
39248 * bound to the model as an array.
39249 * @param {string=} required Sets `required` validation error key if the value is not entered.
39250 * @param {string=} ngRequired Adds required attribute and required validation constraint to
39251 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
39252 * when you want to data-bind to the required attribute.
39253 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
39254 * interaction with the select element.
39255 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
39256 * set on the model on selection. See {@link ngOptions `ngOptions`}.
39259 * ### Simple `select` elements with static options
39261 * <example name="static-select" module="staticSelect">
39262 * <file name="index.html">
39263 * <div ng-controller="ExampleController">
39264 * <form name="myForm">
39265 * <label for="singleSelect"> Single select: </label><br>
39266 * <select name="singleSelect" ng-model="data.singleSelect">
39267 * <option value="option-1">Option 1</option>
39268 * <option value="option-2">Option 2</option>
39271 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
39272 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
39273 * <option value="">---Please select---</option> <!-- not selected / blank option -->
39274 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
39275 * <option value="option-2">Option 2</option>
39277 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
39278 * <tt>singleSelect = {{data.singleSelect}}</tt>
39281 * <label for="multipleSelect"> Multiple select: </label><br>
39282 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
39283 * <option value="option-1">Option 1</option>
39284 * <option value="option-2">Option 2</option>
39285 * <option value="option-3">Option 3</option>
39287 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
39291 * <file name="app.js">
39292 * angular.module('staticSelect', [])
39293 * .controller('ExampleController', ['$scope', function($scope) {
39295 * singleSelect: null,
39296 * multipleSelect: [],
39297 * option1: 'option-1',
39300 * $scope.forceUnknownOption = function() {
39301 * $scope.data.singleSelect = 'nonsense';
39307 * ### Using `ngRepeat` to generate `select` options
39308 * <example name="ngrepeat-select" module="ngrepeatSelect">
39309 * <file name="index.html">
39310 * <div ng-controller="ExampleController">
39311 * <form name="myForm">
39312 * <label for="repeatSelect"> Repeat select: </label>
39313 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
39314 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
39318 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
39321 * <file name="app.js">
39322 * angular.module('ngrepeatSelect', [])
39323 * .controller('ExampleController', ['$scope', function($scope) {
39325 * repeatSelect: null,
39326 * availableOptions: [
39327 * {id: '1', name: 'Option A'},
39328 * {id: '2', name: 'Option B'},
39329 * {id: '3', name: 'Option C'}
39337 * ### Using `select` with `ngOptions` and setting a default value
39338 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
39340 * <example name="select-with-default-values" module="defaultValueSelect">
39341 * <file name="index.html">
39342 * <div ng-controller="ExampleController">
39343 * <form name="myForm">
39344 * <label for="mySelect">Make a choice:</label>
39345 * <select name="mySelect" id="mySelect"
39346 * ng-options="option.name for option in data.availableOptions track by option.id"
39347 * ng-model="data.selectedOption"></select>
39350 * <tt>option = {{data.selectedOption}}</tt><br/>
39353 * <file name="app.js">
39354 * angular.module('defaultValueSelect', [])
39355 * .controller('ExampleController', ['$scope', function($scope) {
39357 * availableOptions: [
39358 * {id: '1', name: 'Option A'},
39359 * {id: '2', name: 'Option B'},
39360 * {id: '3', name: 'Option C'}
39362 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
39369 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
39371 * <example name="select-with-non-string-options" module="nonStringSelect">
39372 * <file name="index.html">
39373 * <select ng-model="model.id" convert-to-number>
39374 * <option value="0">Zero</option>
39375 * <option value="1">One</option>
39376 * <option value="2">Two</option>
39380 * <file name="app.js">
39381 * angular.module('nonStringSelect', [])
39382 * .run(function($rootScope) {
39383 * $rootScope.model = { id: 2 };
39385 * .directive('convertToNumber', function() {
39387 * require: 'ngModel',
39388 * link: function(scope, element, attrs, ngModel) {
39389 * ngModel.$parsers.push(function(val) {
39390 * return parseInt(val, 10);
39392 * ngModel.$formatters.push(function(val) {
39399 * <file name="protractor.js" type="protractor">
39400 * it('should initialize to model', function() {
39401 * var select = element(by.css('select'));
39402 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
39408 var selectDirective = function() {
39412 require: ['select', '?ngModel'],
39413 controller: SelectController,
39416 pre: selectPreLink,
39417 post: selectPostLink
39421 function selectPreLink(scope, element, attr, ctrls) {
39423 // if ngModel is not defined, we don't need to do anything
39424 var ngModelCtrl = ctrls[1];
39425 if (!ngModelCtrl) return;
39427 var selectCtrl = ctrls[0];
39429 selectCtrl.ngModelCtrl = ngModelCtrl;
39431 // When the selected item(s) changes we delegate getting the value of the select control
39432 // to the `readValue` method, which can be changed if the select can have multiple
39433 // selected values or if the options are being generated by `ngOptions`
39434 element.on('change', function() {
39435 scope.$apply(function() {
39436 ngModelCtrl.$setViewValue(selectCtrl.readValue());
39440 // If the select allows multiple values then we need to modify how we read and write
39441 // values from and to the control; also what it means for the value to be empty and
39442 // we have to add an extra watch since ngModel doesn't work well with arrays - it
39443 // doesn't trigger rendering if only an item in the array changes.
39444 if (attr.multiple) {
39446 // Read value now needs to check each option to see if it is selected
39447 selectCtrl.readValue = function readMultipleValue() {
39449 forEach(element.find('option'), function(option) {
39450 if (option.selected) {
39451 array.push(option.value);
39457 // Write value now needs to set the selected property of each matching option
39458 selectCtrl.writeValue = function writeMultipleValue(value) {
39459 var items = new HashMap(value);
39460 forEach(element.find('option'), function(option) {
39461 option.selected = isDefined(items.get(option.value));
39465 // we have to do it on each watch since ngModel watches reference, but
39466 // we need to work of an array, so we need to see if anything was inserted/removed
39467 var lastView, lastViewRef = NaN;
39468 scope.$watch(function selectMultipleWatch() {
39469 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
39470 lastView = shallowCopy(ngModelCtrl.$viewValue);
39471 ngModelCtrl.$render();
39473 lastViewRef = ngModelCtrl.$viewValue;
39476 // If we are a multiple select then value is now a collection
39477 // so the meaning of $isEmpty changes
39478 ngModelCtrl.$isEmpty = function(value) {
39479 return !value || value.length === 0;
39485 function selectPostLink(scope, element, attrs, ctrls) {
39486 // if ngModel is not defined, we don't need to do anything
39487 var ngModelCtrl = ctrls[1];
39488 if (!ngModelCtrl) return;
39490 var selectCtrl = ctrls[0];
39492 // We delegate rendering to the `writeValue` method, which can be changed
39493 // if the select can have multiple selected values or if the options are being
39494 // generated by `ngOptions`.
39495 // This must be done in the postLink fn to prevent $render to be called before
39496 // all nodes have been linked correctly.
39497 ngModelCtrl.$render = function() {
39498 selectCtrl.writeValue(ngModelCtrl.$viewValue);
39504 // The option directive is purely designed to communicate the existence (or lack of)
39505 // of dynamically created (and destroyed) option elements to their containing select
39506 // directive via its controller.
39507 var optionDirective = ['$interpolate', function($interpolate) {
39511 compile: function(element, attr) {
39512 if (isDefined(attr.value)) {
39513 // If the value attribute is defined, check if it contains an interpolation
39514 var interpolateValueFn = $interpolate(attr.value, true);
39516 // If the value attribute is not defined then we fall back to the
39517 // text content of the option element, which may be interpolated
39518 var interpolateTextFn = $interpolate(element.text(), true);
39519 if (!interpolateTextFn) {
39520 attr.$set('value', element.text());
39524 return function(scope, element, attr) {
39525 // This is an optimization over using ^^ since we don't want to have to search
39526 // all the way to the root of the DOM for every single option element
39527 var selectCtrlName = '$selectController',
39528 parent = element.parent(),
39529 selectCtrl = parent.data(selectCtrlName) ||
39530 parent.parent().data(selectCtrlName); // in case we are in optgroup
39533 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
39540 var styleDirective = valueFn({
39551 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39552 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
39553 * applied to custom controls.
39555 * The directive sets the `required` attribute on the element if the Angular expression inside
39556 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
39557 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
39560 * The validator will set the `required` error key to true if the `required` attribute is set and
39561 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
39562 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
39563 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
39564 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
39567 * <example name="ngRequiredDirective" module="ngRequiredExample">
39568 * <file name="index.html">
39570 * angular.module('ngRequiredExample', [])
39571 * .controller('ExampleController', ['$scope', function($scope) {
39572 * $scope.required = true;
39575 * <div ng-controller="ExampleController">
39576 * <form name="form">
39577 * <label for="required">Toggle required: </label>
39578 * <input type="checkbox" ng-model="required" id="required" />
39580 * <label for="input">This input must be filled if `required` is true: </label>
39581 * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
39583 * required error set? = <code>{{form.input.$error.required}}</code><br>
39584 * model = <code>{{model}}</code>
39588 * <file name="protractor.js" type="protractor">
39589 var required = element(by.binding('form.input.$error.required'));
39590 var model = element(by.binding('model'));
39591 var input = element(by.id('input'));
39593 it('should set the required error', function() {
39594 expect(required.getText()).toContain('true');
39596 input.sendKeys('123');
39597 expect(required.getText()).not.toContain('true');
39598 expect(model.getText()).toContain('123');
39603 var requiredDirective = function() {
39606 require: '?ngModel',
39607 link: function(scope, elm, attr, ctrl) {
39609 attr.required = true; // force truthy in case we are on non input element
39611 ctrl.$validators.required = function(modelValue, viewValue) {
39612 return !attr.required || !ctrl.$isEmpty(viewValue);
39615 attr.$observe('required', function() {
39628 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39629 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
39631 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
39632 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
39633 * `ngPattern` attribute value:
39634 * * If the expression evaluates to a RegExp object, then this is used directly.
39635 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
39636 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
39638 * <div class="alert alert-info">
39639 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
39640 * start at the index of the last search's match, thus not taking the whole input value into
39644 * <div class="alert alert-info">
39645 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
39649 * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
39653 * The `ngPattern` attribute must be an expression, while the `pattern` value must be
39660 * <example name="ngPatternDirective" module="ngPatternExample">
39661 * <file name="index.html">
39663 * angular.module('ngPatternExample', [])
39664 * .controller('ExampleController', ['$scope', function($scope) {
39665 * $scope.regex = '\\d+';
39668 * <div ng-controller="ExampleController">
39669 * <form name="form">
39670 * <label for="regex">Set a pattern (regex string): </label>
39671 * <input type="text" ng-model="regex" id="regex" />
39673 * <label for="input">This input is restricted by the current pattern: </label>
39674 * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
39676 * input valid? = <code>{{form.input.$valid}}</code><br>
39677 * model = <code>{{model}}</code>
39681 * <file name="protractor.js" type="protractor">
39682 var model = element(by.binding('model'));
39683 var input = element(by.id('input'));
39685 it('should validate the input with the default pattern', function() {
39686 input.sendKeys('aaa');
39687 expect(model.getText()).not.toContain('aaa');
39689 input.clear().then(function() {
39690 input.sendKeys('123');
39691 expect(model.getText()).toContain('123');
39697 var patternDirective = function() {
39700 require: '?ngModel',
39701 link: function(scope, elm, attr, ctrl) {
39704 var regexp, patternExp = attr.ngPattern || attr.pattern;
39705 attr.$observe('pattern', function(regex) {
39706 if (isString(regex) && regex.length > 0) {
39707 regex = new RegExp('^' + regex + '$');
39710 if (regex && !regex.test) {
39711 throw minErr('ngPattern')('noregexp',
39712 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
39713 regex, startingTag(elm));
39716 regexp = regex || undefined;
39720 ctrl.$validators.pattern = function(modelValue, viewValue) {
39721 // HTML5 pattern constraint validates the input value, so we validate the viewValue
39722 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
39730 * @name ngMaxlength
39734 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39735 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
39737 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
39738 * is longer than the integer obtained by evaluating the Angular expression given in the
39739 * `ngMaxlength` attribute value.
39741 * <div class="alert alert-info">
39742 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
39746 * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
39747 * validation is not available.
39750 * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
39757 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
39758 * <file name="index.html">
39760 * angular.module('ngMaxlengthExample', [])
39761 * .controller('ExampleController', ['$scope', function($scope) {
39762 * $scope.maxlength = 5;
39765 * <div ng-controller="ExampleController">
39766 * <form name="form">
39767 * <label for="maxlength">Set a maxlength: </label>
39768 * <input type="number" ng-model="maxlength" id="maxlength" />
39770 * <label for="input">This input is restricted by the current maxlength: </label>
39771 * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
39773 * input valid? = <code>{{form.input.$valid}}</code><br>
39774 * model = <code>{{model}}</code>
39778 * <file name="protractor.js" type="protractor">
39779 var model = element(by.binding('model'));
39780 var input = element(by.id('input'));
39782 it('should validate the input with the default maxlength', function() {
39783 input.sendKeys('abcdef');
39784 expect(model.getText()).not.toContain('abcdef');
39786 input.clear().then(function() {
39787 input.sendKeys('abcde');
39788 expect(model.getText()).toContain('abcde');
39794 var maxlengthDirective = function() {
39797 require: '?ngModel',
39798 link: function(scope, elm, attr, ctrl) {
39801 var maxlength = -1;
39802 attr.$observe('maxlength', function(value) {
39803 var intVal = toInt(value);
39804 maxlength = isNaN(intVal) ? -1 : intVal;
39807 ctrl.$validators.maxlength = function(modelValue, viewValue) {
39808 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
39816 * @name ngMinlength
39820 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39821 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
39823 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
39824 * is shorter than the integer obtained by evaluating the Angular expression given in the
39825 * `ngMinlength` attribute value.
39827 * <div class="alert alert-info">
39828 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
39832 * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
39833 * validation is not available.
39836 * The `ngMinlength` value must be an expression, while the `minlength` value must be
39843 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
39844 * <file name="index.html">
39846 * angular.module('ngMinlengthExample', [])
39847 * .controller('ExampleController', ['$scope', function($scope) {
39848 * $scope.minlength = 3;
39851 * <div ng-controller="ExampleController">
39852 * <form name="form">
39853 * <label for="minlength">Set a minlength: </label>
39854 * <input type="number" ng-model="minlength" id="minlength" />
39856 * <label for="input">This input is restricted by the current minlength: </label>
39857 * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
39859 * input valid? = <code>{{form.input.$valid}}</code><br>
39860 * model = <code>{{model}}</code>
39864 * <file name="protractor.js" type="protractor">
39865 var model = element(by.binding('model'));
39866 var input = element(by.id('input'));
39868 it('should validate the input with the default minlength', function() {
39869 input.sendKeys('ab');
39870 expect(model.getText()).not.toContain('ab');
39872 input.sendKeys('abc');
39873 expect(model.getText()).toContain('abc');
39878 var minlengthDirective = function() {
39881 require: '?ngModel',
39882 link: function(scope, elm, attr, ctrl) {
39886 attr.$observe('minlength', function(value) {
39887 minlength = toInt(value) || 0;
39890 ctrl.$validators.minlength = function(modelValue, viewValue) {
39891 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
39897 if (window.angular.bootstrap) {
39898 //AngularJS is already loaded, so we can return here...
39899 if (window.console) {
39900 console.log('WARNING: Tried to load angular more than once.');
39905 //try to bind to jquery now so that one can write jqLite(document).ready()
39906 //but we will rebind on bootstrap again.
39909 publishExternalAPI(angular);
39911 angular.module("ngLocale", [], ["$provide", function($provide) {
39912 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
39913 function getDecimals(n) {
39915 var i = n.indexOf('.');
39916 return (i == -1) ? 0 : n.length - i - 1;
39919 function getVF(n, opt_precision) {
39920 var v = opt_precision;
39922 if (undefined === v) {
39923 v = Math.min(getDecimals(n), 3);
39926 var base = Math.pow(10, v);
39927 var f = ((n * base) | 0) % base;
39928 return {v: v, f: f};
39931 $provide.value("$locale", {
39932 "DATETIME_FORMATS": {
39954 "FIRSTDAYOFWEEK": 6,
39992 "STANDALONEMONTH": [
40010 "fullDate": "EEEE, MMMM d, y",
40011 "longDate": "MMMM d, y",
40012 "medium": "MMM d, y h:mm:ss a",
40013 "mediumDate": "MMM d, y",
40014 "mediumTime": "h:mm:ss a",
40015 "short": "M/d/yy h:mm a",
40016 "shortDate": "M/d/yy",
40017 "shortTime": "h:mm a"
40019 "NUMBER_FORMATS": {
40020 "CURRENCY_SYM": "$",
40021 "DECIMAL_SEP": ".",
40041 "negPre": "-\u00a4",
40043 "posPre": "\u00a4",
40049 "localeID": "en_US",
40050 "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;}
40055 * Setup file for the Scenario.
40056 * Must be first in the compilation/bootstrap list.
40059 // Public namespace
40060 angular.scenario = angular.scenario || {};
40063 * Expose jQuery (e.g. for custom dsl extensions).
40065 angular.scenario.jQuery = _jQuery;
40068 * Defines a new output format.
40070 * @param {string} name the name of the new output format
40071 * @param {function()} fn function(context, runner) that generates the output
40073 angular.scenario.output = angular.scenario.output || function(name, fn) {
40074 angular.scenario.output[name] = fn;
40078 * Defines a new DSL statement. If your factory function returns a Future
40079 * it's returned, otherwise the result is assumed to be a map of functions
40080 * for chaining. Chained functions are subject to the same rules.
40082 * Note: All functions on the chain are bound to the chain scope so values
40083 * set on "this" in your statement function are available in the chained
40086 * @param {string} name The name of the statement
40087 * @param {function()} fn Factory function(), return a function for
40090 angular.scenario.dsl = angular.scenario.dsl || function(name, fn) {
40091 angular.scenario.dsl[name] = function() {
40092 /* jshint -W040 *//* The dsl binds `this` for us when calling chained functions */
40093 function executeStatement(statement, args) {
40094 var result = statement.apply(this, args);
40095 if (angular.isFunction(result) || result instanceof angular.scenario.Future) {
40099 var chain = angular.extend({}, result);
40100 angular.forEach(chain, function(value, name) {
40101 if (angular.isFunction(value)) {
40102 chain[name] = function() {
40103 return executeStatement.call(self, value, arguments);
40106 chain[name] = value;
40111 var statement = fn.apply(this, arguments);
40112 return function() {
40113 return executeStatement.call(this, statement, arguments);
40119 * Defines a new matcher for use with the expects() statement. The value
40120 * this.actual (like in Jasmine) is available in your matcher to compare
40121 * against. Your function should return a boolean. The future is automatically
40124 * @param {string} name The name of the matcher
40125 * @param {function()} fn The matching function(expected).
40127 angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
40128 angular.scenario.matcher[name] = function(expected) {
40129 var description = this.future.name +
40130 (this.inverse ? ' not ' : ' ') + name +
40131 ' ' + angular.toJson(expected);
40133 this.addFuture('expect ' + description,
40136 self.actual = self.future.value;
40137 if ((self.inverse && fn.call(self, expected)) ||
40138 (!self.inverse && !fn.call(self, expected))) {
40139 error = 'expected ' + description +
40140 ' but was ' + angular.toJson(self.actual);
40148 * Initialize the scenario runner and run !
40150 * Access global window and document object
40151 * Access $runner through closure
40153 * @param {Object=} config Config options
40155 angular.scenario.setUpAndRun = function(config) {
40156 var href = window.location.href;
40157 var body = _jQuery(window.document.body);
40159 var objModel = new angular.scenario.ObjectModel($runner);
40161 if (config && config.scenario_output) {
40162 output = config.scenario_output.split(',');
40165 angular.forEach(angular.scenario.output, function(fn, name) {
40166 if (!output.length || output.indexOf(name) != -1) {
40167 var context = body.append('<div></div>').find('div:last');
40168 context.attr('id', name);
40169 fn.call({}, context, $runner, objModel);
40173 if (!/^http/.test(href) && !/^https/.test(href)) {
40174 body.append('<p id="system-error"></p>');
40175 body.find('#system-error').text(
40176 'Scenario runner must be run using http or https. The protocol ' +
40177 href.split(':')[0] + ':// is not supported.'
40182 var appFrame = body.append('<div id="application"></div>').find('#application');
40183 var application = new angular.scenario.Application(appFrame);
40185 $runner.on('RunnerEnd', function() {
40186 appFrame.css('display', 'none');
40187 appFrame.find('iframe').attr('src', 'about:blank');
40190 $runner.on('RunnerError', function(error) {
40191 if (window.console) {
40192 console.log(formatException(error));
40194 // Do something for IE
40199 $runner.run(application);
40203 * Iterates through list with iterator function that must call the
40204 * continueFunction to continue iterating.
40206 * @param {Array} list list to iterate over
40207 * @param {function()} iterator Callback function(value, continueFunction)
40208 * @param {function()} done Callback function(error, result) called when
40209 * iteration finishes or an error occurs.
40211 function asyncForEach(list, iterator, done) {
40213 function loop(error, index) {
40214 if (index && index > i) {
40217 if (error || i >= list.length) {
40221 iterator(list[i++], loop);
40231 * Formats an exception into a string with the stack trace, but limits
40232 * to a specific line length.
40234 * @param {Object} error The exception to format, can be anything throwable
40235 * @param {Number=} [maxStackLines=5] max lines of the stack trace to include
40238 function formatException(error, maxStackLines) {
40239 maxStackLines = maxStackLines || 5;
40240 var message = error.toString();
40242 var stack = error.stack.split('\n');
40243 if (stack[0].indexOf(message) === -1) {
40245 stack.unshift(error.message);
40247 message = stack.slice(0, maxStackLines).join('\n');
40253 * Returns a function that gets the file name and line number from a
40254 * location in the stack if available based on the call site.
40256 * Note: this returns another function because accessing .stack is very
40257 * expensive in Chrome.
40259 * @param {Number} offset Number of stack lines to skip
40261 function callerFile(offset) {
40262 var error = new Error();
40264 return function() {
40265 var line = (error.stack || '').split('\n')[offset];
40267 // Clean up the stack trace line
40269 if (line.indexOf('@') !== -1) {
40271 line = line.substring(line.indexOf('@') + 1);
40274 line = line.substring(line.indexOf('(') + 1).replace(')', '');
40284 * Don't use the jQuery trigger method since it works incorrectly.
40286 * jQuery notifies listeners and then changes the state of a checkbox and
40287 * does not create a real browser event. A real click changes the state of
40288 * the checkbox and then notifies listeners.
40290 * To work around this we instead use our own handler that fires a real event.
40293 // We need a handle to the original trigger function for input tests.
40294 var parentTrigger = fn._originalTrigger = fn.trigger;
40295 fn.trigger = function(type) {
40296 if (/(click|change|keydown|blur|input|mousedown|mouseup)/.test(type)) {
40297 var processDefaults = [];
40298 this.each(function(index, node) {
40299 processDefaults.push(browserTrigger(node, type));
40302 // this is not compatible with jQuery - we return an array of returned values,
40303 // so that scenario runner know whether JS code has preventDefault() of the event or not...
40304 return processDefaults;
40306 return parentTrigger.apply(this, arguments);
40311 * Finds all bindings with the substring match of name and returns an
40312 * array of their values.
40314 * @param {string} bindExp The name to match
40315 * @return {Array.<string>} String of binding values
40317 _jQuery.fn.bindings = function(windowJquery, bindExp) {
40318 var result = [], match,
40319 bindSelector = '.ng-binding:visible';
40320 if (angular.isString(bindExp)) {
40321 bindExp = bindExp.replace(/\s/g, '');
40322 match = function(actualExp) {
40324 actualExp = actualExp.replace(/\s/g, '');
40325 if (actualExp == bindExp) return true;
40326 if (actualExp.indexOf(bindExp) === 0) {
40327 return actualExp.charAt(bindExp.length) == '|';
40331 } else if (bindExp) {
40332 match = function(actualExp) {
40333 return actualExp && bindExp.exec(actualExp);
40336 match = function(actualExp) {
40337 return !!actualExp;
40340 var selection = this.find(bindSelector);
40341 if (this.is(bindSelector)) {
40342 selection = selection.add(this);
40345 function push(value) {
40346 if (angular.isUndefined(value)) {
40348 } else if (typeof value !== 'string') {
40349 value = angular.toJson(value);
40351 result.push('' + value);
40354 selection.each(function() {
40355 var element = windowJquery(this),
40357 if (bindings = element.data('$binding')) {
40358 for (var expressions = [], binding, j=0, jj=bindings.length; j < jj; j++) {
40359 binding = bindings[j];
40361 if (binding.expressions) {
40362 expressions = binding.expressions;
40364 expressions = [binding];
40366 for (var scope, expression, i = 0, ii = expressions.length; i < ii; i++) {
40367 expression = expressions[i];
40368 if (match(expression)) {
40369 scope = scope || element.scope();
40370 push(scope.$eval(expression));
40381 * Triggers a browser event. Attempts to choose the right event if one is
40384 * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
40385 * @param {string} eventType Optional event type
40386 * @param {Object=} eventData An optional object which contains additional event data (such as x,y
40387 * coordinates, keys, etc...) that are passed into the event when triggered
40389 window.browserTrigger = function browserTrigger(element, eventType, eventData) {
40390 if (element && !element.nodeName) element = element[0];
40391 if (!element) return;
40393 eventData = eventData || {};
40394 var relatedTarget = eventData.relatedTarget || element;
40395 var keys = eventData.keys;
40396 var x = eventData.x;
40397 var y = eventData.y;
40399 var inputType = (element.type) ? element.type.toLowerCase() : null,
40400 nodeName = element.nodeName.toLowerCase();
40404 'textarea': 'change',
40405 'hidden': 'change',
40406 'password': 'change',
40411 'checkbox': 'click',
40413 'select-one': 'change',
40414 'select-multiple': 'change',
40415 '_default_': 'click'
40416 }[inputType || '_default_'];
40419 if (nodeName == 'option') {
40420 element.parentNode.value = element.value;
40421 element = element.parentNode;
40422 eventType = 'change';
40426 function pressed(key) {
40427 return keys.indexOf(key) !== -1;
40431 if (/transitionend/.test(eventType)) {
40432 if (window.WebKitTransitionEvent) {
40433 evnt = new WebKitTransitionEvent(eventType, eventData);
40434 evnt.initEvent(eventType, false, true);
40437 evnt = new TransitionEvent(eventType, eventData);
40440 evnt = window.document.createEvent('TransitionEvent');
40441 evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
40444 } else if (/animationend/.test(eventType)) {
40445 if (window.WebKitAnimationEvent) {
40446 evnt = new WebKitAnimationEvent(eventType, eventData);
40447 evnt.initEvent(eventType, false, true);
40450 evnt = new AnimationEvent(eventType, eventData);
40453 evnt = window.document.createEvent('AnimationEvent');
40454 evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
40457 } else if (/touch/.test(eventType) && supportsTouchEvents()) {
40458 evnt = createTouchEvent(element, eventType, x, y);
40460 evnt = window.document.createEvent('MouseEvents');
40463 evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
40464 pressed('alt'), pressed('shift'), pressed('meta'), 0, relatedTarget);
40467 /* we're unable to change the timeStamp value directly so this
40468 * is only here to allow for testing where the timeStamp value is
40470 evnt.$manualTimeStamp = eventData.timeStamp;
40474 var originalPreventDefault = evnt.preventDefault,
40475 appWindow = element.ownerDocument.defaultView,
40476 fakeProcessDefault = true,
40477 finalProcessDefault,
40478 angular = appWindow.angular || {};
40480 // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
40481 angular['ff-684208-preventDefault'] = false;
40482 evnt.preventDefault = function() {
40483 fakeProcessDefault = false;
40484 return originalPreventDefault.apply(evnt, arguments);
40487 element.dispatchEvent(evnt);
40488 finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
40490 delete angular['ff-684208-preventDefault'];
40492 return finalProcessDefault;
40495 function supportsTouchEvents() {
40496 if ('_cached' in supportsTouchEvents) {
40497 return supportsTouchEvents._cached;
40499 if (!window.document.createTouch || !window.document.createTouchList) {
40500 supportsTouchEvents._cached = false;
40504 window.document.createEvent('TouchEvent');
40506 supportsTouchEvents._cached = false;
40509 supportsTouchEvents._cached = true;
40513 function createTouchEvent(element, eventType, x, y) {
40514 var evnt = new window.Event(eventType);
40518 var touch = window.document.createTouch(window, element, Date.now(), x, y, x, y);
40519 var touches = window.document.createTouchList(touch);
40521 evnt.touches = touches;
40528 * Represents the application currently being tested and abstracts usage
40529 * of iframes or separate windows.
40531 * @param {Object} context jQuery wrapper around HTML context.
40533 angular.scenario.Application = function(context) {
40534 this.context = context;
40536 '<h2>Current URL: <a href="about:blank">None</a></h2>' +
40537 '<div id="test-frames"></div>'
40542 * Gets the jQuery collection of frames. Don't use this directly because
40543 * frames may go stale.
40546 * @return {Object} jQuery collection
40548 angular.scenario.Application.prototype.getFrame_ = function() {
40549 return this.context.find('#test-frames iframe:last');
40553 * Gets the window of the test runner frame. Always favor executeAction()
40554 * instead of this method since it prevents you from getting a stale window.
40557 * @return {Object} the window of the frame
40559 angular.scenario.Application.prototype.getWindow_ = function() {
40560 var contentWindow = this.getFrame_().prop('contentWindow');
40561 if (!contentWindow) {
40562 throw 'Frame window is not accessible.';
40564 return contentWindow;
40568 * Changes the location of the frame.
40570 * @param {string} url The URL. If it begins with a # then only the
40571 * hash of the page is changed.
40572 * @param {function()} loadFn function($window, $document) Called when frame loads.
40573 * @param {function()} errorFn function(error) Called if any error when loading.
40575 angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorFn) {
40577 var frame = self.getFrame_();
40578 //TODO(esprehn): Refactor to use rethrow()
40579 errorFn = errorFn || function(e) { throw e; };
40580 if (url === 'about:blank') {
40581 errorFn('Sandbox Error: Navigating to about:blank is not allowed.');
40582 } else if (url.charAt(0) === '#') {
40583 url = frame.attr('src').split('#')[0] + url;
40584 frame.attr('src', url);
40585 self.executeAction(loadFn);
40588 self.context.find('#test-frames').append('<iframe>');
40589 frame = self.getFrame_();
40591 frame.on('load', function() {
40594 var $window = self.getWindow_();
40596 if (!$window.angular) {
40597 self.executeAction(loadFn);
40601 if (!$window.angular.resumeBootstrap) {
40602 $window.angular.resumeDeferredBootstrap = resumeDeferredBootstrap;
40604 resumeDeferredBootstrap();
40611 function resumeDeferredBootstrap() {
40612 // Disable animations
40613 var $injector = $window.angular.resumeBootstrap([['$provide', function($provide) {
40614 return ['$animate', function($animate) {
40615 $animate.enabled(false);
40618 self.rootElement = $injector.get('$rootElement')[0];
40619 self.executeAction(loadFn);
40621 }).attr('src', url);
40623 // for IE compatibility set the name *after* setting the frame url
40624 frame[0].contentWindow.name = "NG_DEFER_BOOTSTRAP!";
40626 self.context.find('> h2 a').attr('href', url).text(url);
40630 * Executes a function in the context of the tested application. Will wait
40631 * for all pending angular xhr requests before executing.
40633 * @param {function()} action The callback to execute. function($window, $document)
40634 * $document is a jQuery wrapped document.
40636 angular.scenario.Application.prototype.executeAction = function(action) {
40638 var $window = this.getWindow_();
40639 if (!$window.document) {
40640 throw 'Sandbox Error: Application document not accessible.';
40642 if (!$window.angular) {
40643 return action.call(this, $window, _jQuery($window.document));
40646 if (!!this.rootElement) {
40647 executeWithElement(this.rootElement);
40649 angularInit($window.document, angular.bind(this, executeWithElement));
40652 function executeWithElement(element) {
40653 var $injector = $window.angular.element(element).injector();
40654 var $element = _jQuery(element);
40656 $element.injector = function() {
40660 $injector.invoke(function($browser) {
40661 $browser.notifyWhenNoOutstandingRequests(function() {
40662 action.call(self, $window, $element);
40669 * The representation of define blocks. Don't used directly, instead use
40670 * define() in your tests.
40672 * @param {string} descName Name of the block
40673 * @param {Object} parent describe or undefined if the root.
40675 angular.scenario.Describe = function(descName, parent) {
40676 this.only = parent && parent.only;
40677 this.beforeEachFns = [];
40678 this.afterEachFns = [];
40680 this.children = [];
40681 this.name = descName;
40682 this.parent = parent;
40683 this.id = angular.scenario.Describe.id++;
40686 * Calls all before functions.
40688 var beforeEachFns = this.beforeEachFns;
40689 this.setupBefore = function() {
40690 if (parent) parent.setupBefore.call(this);
40691 angular.forEach(beforeEachFns, function(fn) { fn.call(this); }, this);
40695 * Calls all after functions.
40697 var afterEachFns = this.afterEachFns;
40698 this.setupAfter = function() {
40699 angular.forEach(afterEachFns, function(fn) { fn.call(this); }, this);
40700 if (parent) parent.setupAfter.call(this);
40704 // Shared Unique ID generator for every describe block
40705 angular.scenario.Describe.id = 0;
40707 // Shared Unique ID generator for every it (spec)
40708 angular.scenario.Describe.specId = 0;
40711 * Defines a block to execute before each it or nested describe.
40713 * @param {function()} body Body of the block.
40715 angular.scenario.Describe.prototype.beforeEach = function(body) {
40716 this.beforeEachFns.push(body);
40720 * Defines a block to execute after each it or nested describe.
40722 * @param {function()} body Body of the block.
40724 angular.scenario.Describe.prototype.afterEach = function(body) {
40725 this.afterEachFns.push(body);
40729 * Creates a new describe block that's a child of this one.
40731 * @param {string} name Name of the block. Appended to the parent block's name.
40732 * @param {function()} body Body of the block.
40734 angular.scenario.Describe.prototype.describe = function(name, body) {
40735 var child = new angular.scenario.Describe(name, this);
40736 this.children.push(child);
40741 * Same as describe() but makes ddescribe blocks the only to run.
40743 * @param {string} name Name of the test.
40744 * @param {function()} body Body of the block.
40746 angular.scenario.Describe.prototype.ddescribe = function(name, body) {
40747 var child = new angular.scenario.Describe(name, this);
40749 this.children.push(child);
40754 * Use to disable a describe block.
40756 angular.scenario.Describe.prototype.xdescribe = angular.noop;
40761 * @param {string} name Name of the test.
40762 * @param {function()} body Body of the block.
40764 angular.scenario.Describe.prototype.it = function(name, body) {
40766 id: angular.scenario.Describe.specId++,
40770 before: this.setupBefore,
40772 after: this.setupAfter
40777 * Same as it() but makes iit tests the only test to run.
40779 * @param {string} name Name of the test.
40780 * @param {function()} body Body of the block.
40782 angular.scenario.Describe.prototype.iit = function(name, body) {
40783 this.it.apply(this, arguments);
40784 this.its[this.its.length - 1].only = true;
40788 * Use to disable a test block.
40790 angular.scenario.Describe.prototype.xit = angular.noop;
40793 * Gets an array of functions representing all the tests (recursively).
40794 * that can be executed with SpecRunner's.
40796 * @return {Array<Object>} Array of it blocks {
40797 * definition : Object // parent Describe
40805 angular.scenario.Describe.prototype.getSpecs = function() {
40806 var specs = arguments[0] || [];
40807 angular.forEach(this.children, function(child) {
40808 child.getSpecs(specs);
40810 angular.forEach(this.its, function(it) {
40814 angular.forEach(specs, function(it) {
40819 return (only.length && only) || specs;
40823 * A future action in a spec.
40825 * @param {string} name name of the future action
40826 * @param {function()} behavior future callback(error, result)
40827 * @param {function()} line Optional. function that returns the file/line number.
40829 angular.scenario.Future = function(name, behavior, line) {
40831 this.behavior = behavior;
40832 this.fulfilled = false;
40833 this.value = undefined;
40834 this.parser = angular.identity;
40835 this.line = line || function() { return ''; };
40839 * Executes the behavior of the closure.
40841 * @param {function()} doneFn Callback function(error, result)
40843 angular.scenario.Future.prototype.execute = function(doneFn) {
40845 this.behavior(function(error, result) {
40846 self.fulfilled = true;
40849 result = self.parser(result);
40854 self.value = error || result;
40855 doneFn(error, result);
40860 * Configures the future to convert its final with a function fn(value)
40862 * @param {function()} fn function(value) that returns the parsed value
40864 angular.scenario.Future.prototype.parsedWith = function(fn) {
40870 * Configures the future to parse its final value from JSON
40873 angular.scenario.Future.prototype.fromJson = function() {
40874 return this.parsedWith(angular.fromJson);
40878 * Configures the future to convert its final value from objects
40881 angular.scenario.Future.prototype.toJson = function() {
40882 return this.parsedWith(angular.toJson);
40886 * Maintains an object tree from the runner events.
40888 * @param {Object} runner The scenario Runner instance to connect to.
40890 * TODO(esprehn): Every output type creates one of these, but we probably
40891 * want one global shared instance. Need to handle events better too
40892 * so the HTML output doesn't need to do spec model.getSpec(spec.id)
40895 * TODO(vojta) refactor on, emit methods (from all objects) - use inheritance
40897 angular.scenario.ObjectModel = function(runner) {
40901 this.listeners = [];
40907 runner.on('SpecBegin', function(spec) {
40908 var block = self.value,
40911 angular.forEach(self.getDefinitionPath(spec), function(def) {
40912 if (!block.children[def.name]) {
40913 block.children[def.name] = {
40920 block = block.children[def.name];
40921 definitions.push(def.name);
40924 var it = self.specMap[spec.id] =
40925 block.specs[spec.name] =
40926 new angular.scenario.ObjectModel.Spec(spec.id, spec.name, definitions);
40928 // forward the event
40929 self.emit('SpecBegin', it);
40932 runner.on('SpecError', function(spec, error) {
40933 var it = self.getSpec(spec.id);
40934 it.status = 'error';
40937 // forward the event
40938 self.emit('SpecError', it, error);
40941 runner.on('SpecEnd', function(spec) {
40942 var it = self.getSpec(spec.id);
40945 // forward the event
40946 self.emit('SpecEnd', it);
40949 runner.on('StepBegin', function(spec, step) {
40950 var it = self.getSpec(spec.id);
40951 step = new angular.scenario.ObjectModel.Step(step.name);
40952 it.steps.push(step);
40954 // forward the event
40955 self.emit('StepBegin', it, step);
40958 runner.on('StepEnd', function(spec) {
40959 var it = self.getSpec(spec.id);
40960 var step = it.getLastStep();
40961 if (step.name !== step.name) {
40962 throw 'Events fired in the wrong order. Step names don\'t match.';
40966 // forward the event
40967 self.emit('StepEnd', it, step);
40970 runner.on('StepFailure', function(spec, step, error) {
40971 var it = self.getSpec(spec.id),
40972 modelStep = it.getLastStep();
40974 modelStep.setErrorStatus('failure', error, step.line());
40975 it.setStatusFromStep(modelStep);
40977 // forward the event
40978 self.emit('StepFailure', it, modelStep, error);
40981 runner.on('StepError', function(spec, step, error) {
40982 var it = self.getSpec(spec.id),
40983 modelStep = it.getLastStep();
40985 modelStep.setErrorStatus('error', error, step.line());
40986 it.setStatusFromStep(modelStep);
40988 // forward the event
40989 self.emit('StepError', it, modelStep, error);
40992 runner.on('RunnerBegin', function() {
40993 self.emit('RunnerBegin');
40995 runner.on('RunnerEnd', function() {
40996 self.emit('RunnerEnd');
40999 function complete(item) {
41000 item.endTime = Date.now();
41001 item.duration = item.endTime - item.startTime;
41002 item.status = item.status || 'success';
41007 * Adds a listener for an event.
41009 * @param {string} eventName Name of the event to add a handler for
41010 * @param {function()} listener Function that will be called when event is fired
41012 angular.scenario.ObjectModel.prototype.on = function(eventName, listener) {
41013 eventName = eventName.toLowerCase();
41014 this.listeners[eventName] = this.listeners[eventName] || [];
41015 this.listeners[eventName].push(listener);
41019 * Emits an event which notifies listeners and passes extra
41022 * @param {string} eventName Name of the event to fire.
41024 angular.scenario.ObjectModel.prototype.emit = function(eventName) {
41026 args = Array.prototype.slice.call(arguments, 1);
41028 eventName = eventName.toLowerCase();
41030 if (this.listeners[eventName]) {
41031 angular.forEach(this.listeners[eventName], function(listener) {
41032 listener.apply(self, args);
41038 * Computes the path of definition describe blocks that wrap around
41041 * @param spec Spec to compute the path for.
41042 * @return {Array<Describe>} The describe block path
41044 angular.scenario.ObjectModel.prototype.getDefinitionPath = function(spec) {
41046 var currentDefinition = spec.definition;
41047 while (currentDefinition && currentDefinition.name) {
41048 path.unshift(currentDefinition);
41049 currentDefinition = currentDefinition.parent;
41055 * Gets a spec by id.
41057 * @param {string} id The id of the spec to get the object for.
41058 * @return {Object} the Spec instance
41060 angular.scenario.ObjectModel.prototype.getSpec = function(id) {
41061 return this.specMap[id];
41065 * A single it block.
41067 * @param {string} id Id of the spec
41068 * @param {string} name Name of the spec
41069 * @param {Array<string>=} definitionNames List of all describe block names that wrap this spec
41071 angular.scenario.ObjectModel.Spec = function(id, name, definitionNames) {
41074 this.startTime = Date.now();
41076 this.fullDefinitionName = (definitionNames || []).join(' ');
41080 * Adds a new step to the Spec.
41082 * @param {string} name Name of the step (really name of the future)
41083 * @return {Object} the added step
41085 angular.scenario.ObjectModel.Spec.prototype.addStep = function(name) {
41086 var step = new angular.scenario.ObjectModel.Step(name);
41087 this.steps.push(step);
41092 * Gets the most recent step.
41094 * @return {Object} the step
41096 angular.scenario.ObjectModel.Spec.prototype.getLastStep = function() {
41097 return this.steps[this.steps.length - 1];
41101 * Set status of the Spec from given Step
41103 * @param {angular.scenario.ObjectModel.Step} step
41105 angular.scenario.ObjectModel.Spec.prototype.setStatusFromStep = function(step) {
41106 if (!this.status || step.status == 'error') {
41107 this.status = step.status;
41108 this.error = step.error;
41109 this.line = step.line;
41114 * A single step inside a Spec.
41116 * @param {string} name Name of the step
41118 angular.scenario.ObjectModel.Step = function(name) {
41120 this.startTime = Date.now();
41124 * Helper method for setting all error status related properties
41126 * @param {string} status
41127 * @param {string} error
41128 * @param {string} line
41130 angular.scenario.ObjectModel.Step.prototype.setErrorStatus = function(status, error, line) {
41131 this.status = status;
41132 this.error = error;
41137 * Runner for scenarios
41139 * Has to be initialized before any test is loaded,
41140 * because it publishes the API into window (global space).
41142 angular.scenario.Runner = function($window) {
41143 this.listeners = [];
41144 this.$window = $window;
41145 this.rootDescribe = new angular.scenario.Describe();
41146 this.currentDescribe = this.rootDescribe;
41151 describe: this.describe,
41152 ddescribe: this.ddescribe,
41153 xdescribe: angular.noop,
41154 beforeEach: this.beforeEach,
41155 afterEach: this.afterEach
41157 angular.forEach(this.api, angular.bind(this, function(fn, key) {
41158 this.$window[key] = angular.bind(this, fn);
41163 * Emits an event which notifies listeners and passes extra
41166 * @param {string} eventName Name of the event to fire.
41168 angular.scenario.Runner.prototype.emit = function(eventName) {
41170 var args = Array.prototype.slice.call(arguments, 1);
41171 eventName = eventName.toLowerCase();
41172 if (!this.listeners[eventName]) {
41175 angular.forEach(this.listeners[eventName], function(listener) {
41176 listener.apply(self, args);
41181 * Adds a listener for an event.
41183 * @param {string} eventName The name of the event to add a handler for
41184 * @param {string} listener The fn(...) that takes the extra arguments from emit()
41186 angular.scenario.Runner.prototype.on = function(eventName, listener) {
41187 eventName = eventName.toLowerCase();
41188 this.listeners[eventName] = this.listeners[eventName] || [];
41189 this.listeners[eventName].push(listener);
41193 * Defines a describe block of a spec.
41197 * @param {string} name Name of the block
41198 * @param {function()} body Body of the block
41200 angular.scenario.Runner.prototype.describe = function(name, body) {
41202 this.currentDescribe.describe(name, function() {
41203 var parentDescribe = self.currentDescribe;
41204 self.currentDescribe = this;
41208 self.currentDescribe = parentDescribe;
41214 * Same as describe, but makes ddescribe the only blocks to run.
41218 * @param {string} name Name of the block
41219 * @param {function()} body Body of the block
41221 angular.scenario.Runner.prototype.ddescribe = function(name, body) {
41223 this.currentDescribe.ddescribe(name, function() {
41224 var parentDescribe = self.currentDescribe;
41225 self.currentDescribe = this;
41229 self.currentDescribe = parentDescribe;
41235 * Defines a test in a describe block of a spec.
41239 * @param {string} name Name of the block
41240 * @param {function()} body Body of the block
41242 angular.scenario.Runner.prototype.it = function(name, body) {
41243 this.currentDescribe.it(name, body);
41247 * Same as it, but makes iit tests the only tests to run.
41251 * @param {string} name Name of the block
41252 * @param {function()} body Body of the block
41254 angular.scenario.Runner.prototype.iit = function(name, body) {
41255 this.currentDescribe.iit(name, body);
41259 * Defines a function to be called before each it block in the describe
41260 * (and before all nested describes).
41264 * @param {function()} Callback to execute
41266 angular.scenario.Runner.prototype.beforeEach = function(body) {
41267 this.currentDescribe.beforeEach(body);
41271 * Defines a function to be called after each it block in the describe
41272 * (and before all nested describes).
41276 * @param {function()} Callback to execute
41278 angular.scenario.Runner.prototype.afterEach = function(body) {
41279 this.currentDescribe.afterEach(body);
41283 * Creates a new spec runner.
41286 * @param {Object} scope parent scope
41288 angular.scenario.Runner.prototype.createSpecRunner_ = function(scope) {
41289 var child = scope.$new();
41290 var Cls = angular.scenario.SpecRunner;
41292 // Export all the methods to child scope manually as now we don't mess controllers with scopes
41293 // TODO(vojta): refactor scenario runner so that these objects are not tightly coupled as current
41294 for (var name in Cls.prototype) {
41295 child[name] = angular.bind(child, Cls.prototype[name]);
41303 * Runs all the loaded tests with the specified runner class on the
41304 * provided application.
41306 * @param {angular.scenario.Application} application App to remote control.
41308 angular.scenario.Runner.prototype.run = function(application) {
41310 var $root = angular.injector(['ng']).get('$rootScope');
41311 angular.extend($root, this);
41312 angular.forEach(angular.scenario.Runner.prototype, function(fn, name) {
41313 $root[name] = angular.bind(self, fn);
41315 $root.application = application;
41316 $root.emit('RunnerBegin');
41317 asyncForEach(this.rootDescribe.getSpecs(), function(spec, specDone) {
41319 var runner = self.createSpecRunner_($root);
41320 angular.forEach(angular.scenario.dsl, function(fn, key) {
41321 dslCache[key] = fn.call($root);
41323 angular.forEach(angular.scenario.dsl, function(fn, key) {
41324 self.$window[key] = function() {
41325 var line = callerFile(3);
41326 var scope = runner.$new();
41328 // Make the dsl accessible on the current chain
41330 angular.forEach(dslCache, function(fn, key) {
41331 scope.dsl[key] = function() {
41332 return dslCache[key].apply(scope, arguments);
41336 // Make these methods work on the current chain
41337 scope.addFuture = function() {
41338 Array.prototype.push.call(arguments, line);
41339 return angular.scenario.SpecRunner.
41340 prototype.addFuture.apply(scope, arguments);
41342 scope.addFutureAction = function() {
41343 Array.prototype.push.call(arguments, line);
41344 return angular.scenario.SpecRunner.
41345 prototype.addFutureAction.apply(scope, arguments);
41348 return scope.dsl[key].apply(scope, arguments);
41351 runner.run(spec, function() {
41353 specDone.apply(this, arguments);
41358 self.emit('RunnerError', error);
41360 self.emit('RunnerEnd');
41365 * This class is the "this" of the it/beforeEach/afterEach method.
41366 * Responsibilities:
41367 * - "this" for it/beforeEach/afterEach
41368 * - keep state for single it/beforeEach/afterEach execution
41369 * - keep track of all of the futures to execute
41370 * - run single spec (execute each future)
41372 angular.scenario.SpecRunner = function() {
41374 this.afterIndex = 0;
41378 * Executes a spec which is an it block with associated before/after functions
41379 * based on the describe nesting.
41381 * @param {Object} spec A spec object
41382 * @param {function()} specDone function that is called when the spec finishes,
41383 * of the form `Function(error, index)`
41385 angular.scenario.SpecRunner.prototype.run = function(spec, specDone) {
41389 this.emit('SpecBegin', spec);
41392 spec.before.call(this);
41393 spec.body.call(this);
41394 this.afterIndex = this.futures.length;
41395 spec.after.call(this);
41397 this.emit('SpecError', spec, e);
41398 this.emit('SpecEnd', spec);
41403 var handleError = function(error, done) {
41408 done(null, self.afterIndex);
41413 function(future, futureDone) {
41414 self.step = future;
41415 self.emit('StepBegin', spec, future);
41417 future.execute(function(error) {
41419 self.emit('StepFailure', spec, future, error);
41420 self.emit('StepEnd', spec, future);
41421 return handleError(error, futureDone);
41423 self.emit('StepEnd', spec, future);
41424 self.$window.setTimeout(function() { futureDone(); }, 0);
41427 self.emit('StepError', spec, future, e);
41428 self.emit('StepEnd', spec, future);
41429 handleError(e, futureDone);
41434 self.emit('SpecError', spec, e);
41436 self.emit('SpecEnd', spec);
41437 // Call done in a timeout so exceptions don't recursively
41438 // call this function
41439 self.$window.setTimeout(function() { specDone(); }, 0);
41445 * Adds a new future action.
41447 * Note: Do not pass line manually. It happens automatically.
41449 * @param {string} name Name of the future
41450 * @param {function()} behavior Behavior of the future
41451 * @param {function()} line fn() that returns file/line number
41453 angular.scenario.SpecRunner.prototype.addFuture = function(name, behavior, line) {
41454 var future = new angular.scenario.Future(name, angular.bind(this, behavior), line);
41455 this.futures.push(future);
41460 * Adds a new future action to be executed on the application window.
41462 * Note: Do not pass line manually. It happens automatically.
41464 * @param {string} name Name of the future
41465 * @param {function()} behavior Behavior of the future
41466 * @param {function()} line fn() that returns file/line number
41468 angular.scenario.SpecRunner.prototype.addFutureAction = function(name, behavior, line) {
41470 var NG = /\[ng\\\:/;
41471 return this.addFuture(name, function(done) {
41472 this.application.executeAction(function($window, $document) {
41474 //TODO(esprehn): Refactor this so it doesn't need to be in here.
41475 $document.elements = function(selector) {
41476 var args = Array.prototype.slice.call(arguments, 1);
41477 selector = (self.selector || '') + ' ' + (selector || '');
41478 selector = _jQuery.trim(selector) || '*';
41479 angular.forEach(args, function(value, index) {
41480 selector = selector.replace('$' + (index + 1), value);
41482 var result = $document.find(selector);
41483 if (selector.match(NG)) {
41484 angular.forEach(['[ng-','[data-ng-','[x-ng-'], function(value, index) {
41485 result = result.add(selector.replace(NG, value), $document);
41488 if (!result.length) {
41491 message: 'Selector ' + selector + ' did not match any elements.'
41499 behavior.call(self, $window, $document, done);
41501 if (e.type && e.type === 'selector') {
41512 * Shared DSL statements that are useful to all scenarios.
41517 * pause() pauses until you call resume() in the console
41519 angular.scenario.dsl('pause', function() {
41520 return function() {
41521 return this.addFuture('pausing for you to resume', function(done) {
41522 this.emit('InteractivePause', this.spec, this.step);
41523 this.$window.resume = function() { done(); };
41530 * sleep(seconds) pauses the test for specified number of seconds
41532 angular.scenario.dsl('sleep', function() {
41533 return function(time) {
41534 return this.addFuture('sleep for ' + time + ' seconds', function(done) {
41535 this.$window.setTimeout(function() { done(null, time * 1000); }, time * 1000);
41542 * browser().navigateTo(url) Loads the url into the frame
41543 * browser().navigateTo(url, fn) where fn(url) is called and returns the URL to navigate to
41544 * browser().reload() refresh the page (reload the same URL)
41545 * browser().window.href() window.location.href
41546 * browser().window.path() window.location.pathname
41547 * browser().window.search() window.location.search
41548 * browser().window.hash() window.location.hash without # prefix
41549 * browser().location().url() see ng.$location#url
41550 * browser().location().path() see ng.$location#path
41551 * browser().location().search() see ng.$location#search
41552 * browser().location().hash() see ng.$location#hash
41554 angular.scenario.dsl('browser', function() {
41557 chain.navigateTo = function(url, delegate) {
41558 var application = this.application;
41559 return this.addFuture("browser navigate to '" + url + "'", function(done) {
41561 url = delegate.call(this, url);
41563 application.navigateTo(url, function() {
41569 chain.reload = function() {
41570 var application = this.application;
41571 return this.addFutureAction('browser reload', function($window, $document, done) {
41572 var href = $window.location.href;
41573 application.navigateTo(href, function() {
41579 chain.window = function() {
41582 api.href = function() {
41583 return this.addFutureAction('window.location.href', function($window, $document, done) {
41584 done(null, $window.location.href);
41588 api.path = function() {
41589 return this.addFutureAction('window.location.path', function($window, $document, done) {
41590 done(null, $window.location.pathname);
41594 api.search = function() {
41595 return this.addFutureAction('window.location.search', function($window, $document, done) {
41596 done(null, $window.location.search);
41600 api.hash = function() {
41601 return this.addFutureAction('window.location.hash', function($window, $document, done) {
41602 done(null, $window.location.hash.replace('#', ''));
41609 chain.location = function() {
41612 api.url = function() {
41613 return this.addFutureAction('$location.url()', function($window, $document, done) {
41614 done(null, $document.injector().get('$location').url());
41618 api.path = function() {
41619 return this.addFutureAction('$location.path()', function($window, $document, done) {
41620 done(null, $document.injector().get('$location').path());
41624 api.search = function() {
41625 return this.addFutureAction('$location.search()', function($window, $document, done) {
41626 done(null, $document.injector().get('$location').search());
41630 api.hash = function() {
41631 return this.addFutureAction('$location.hash()', function($window, $document, done) {
41632 done(null, $document.injector().get('$location').hash());
41639 return function() {
41646 * expect(future).{matcher} where matcher is one of the matchers defined
41647 * with angular.scenario.matcher
41649 * ex. expect(binding("name")).toEqual("Elliott")
41651 angular.scenario.dsl('expect', function() {
41652 var chain = angular.extend({}, angular.scenario.matcher);
41654 chain.not = function() {
41655 this.inverse = true;
41659 return function(future) {
41660 this.future = future;
41667 * using(selector, label) scopes the next DSL element selection
41670 * using('#foo', "'Foo' text field").input('bar')
41672 angular.scenario.dsl('using', function() {
41673 return function(selector, label) {
41674 this.selector = _jQuery.trim((this.selector || '') + ' ' + selector);
41675 if (angular.isString(label) && label.length) {
41676 this.label = label + ' ( ' + this.selector + ' )';
41678 this.label = this.selector;
41686 * binding(name) returns the value of the first matching binding
41688 angular.scenario.dsl('binding', function() {
41689 return function(name) {
41690 return this.addFutureAction("select binding '" + name + "'",
41691 function($window, $document, done) {
41692 var values = $document.elements().bindings($window.angular.element, name);
41693 if (!values.length) {
41694 return done("Binding selector '" + name + "' did not match.");
41696 done(null, values[0]);
41703 * input(name).enter(value) enters value in input with specified name
41704 * input(name).check() checks checkbox
41705 * input(name).select(value) selects the radio button with specified name/value
41706 * input(name).val() returns the value of the input.
41708 angular.scenario.dsl('input', function() {
41710 var supportInputEvent = 'oninput' in window.document.createElement('div') && !msie;
41712 chain.enter = function(value, event) {
41713 return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",
41714 function($window, $document, done) {
41715 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
41717 input.trigger(event || (supportInputEvent ? 'input' : 'change'));
41722 chain.check = function() {
41723 return this.addFutureAction("checkbox '" + this.name + "' toggle",
41724 function($window, $document, done) {
41725 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':checkbox');
41726 input.trigger('click');
41731 chain.select = function(value) {
41732 return this.addFutureAction("radio button '" + this.name + "' toggle '" + value + "'",
41733 function($window, $document, done) {
41734 var input = $document.
41735 elements('[ng\\:model="$1"][value="$2"]', this.name, value).filter(':radio');
41736 input.trigger('click');
41741 chain.val = function() {
41742 return this.addFutureAction("return input val", function($window, $document, done) {
41743 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
41744 done(null,input.val());
41748 return function(name) {
41757 * repeater('#products table', 'Product List').count() number of rows
41758 * repeater('#products table', 'Product List').row(1) all bindings in row as an array
41759 * repeater('#products table', 'Product List').column('product.name') all values across all rows
41762 angular.scenario.dsl('repeater', function() {
41765 chain.count = function() {
41766 return this.addFutureAction("repeater '" + this.label + "' count",
41767 function($window, $document, done) {
41769 done(null, $document.elements().length);
41776 chain.column = function(binding) {
41777 return this.addFutureAction("repeater '" + this.label + "' column '" + binding + "'",
41778 function($window, $document, done) {
41779 done(null, $document.elements().bindings($window.angular.element, binding));
41783 chain.row = function(index) {
41784 return this.addFutureAction("repeater '" + this.label + "' row '" + index + "'",
41785 function($window, $document, done) {
41786 var matches = $document.elements().slice(index, index + 1);
41787 if (!matches.length) {
41788 return done('row ' + index + ' out of bounds');
41790 done(null, matches.bindings($window.angular.element));
41794 return function(selector, label) {
41795 this.dsl.using(selector, label);
41802 * select(name).option('value') select one option
41803 * select(name).options('value1', 'value2', ...) select options from a multi select
41805 angular.scenario.dsl('select', function() {
41808 chain.option = function(value) {
41809 return this.addFutureAction("select '" + this.name + "' option '" + value + "'",
41810 function($window, $document, done) {
41811 var select = $document.elements('select[ng\\:model="$1"]', this.name);
41812 var option = select.find('option[value="' + value + '"]');
41813 if (option.length) {
41816 option = select.find('option').filter(function() {
41817 return _jQuery(this).text() === value;
41819 if (!option.length) {
41820 option = select.find('option:contains("' + value + '")');
41822 if (option.length) {
41823 select.val(option.val());
41825 return done("option '" + value + "' not found");
41828 select.trigger('change');
41833 chain.options = function() {
41834 var values = arguments;
41835 return this.addFutureAction("select '" + this.name + "' options '" + values + "'",
41836 function($window, $document, done) {
41837 var select = $document.elements('select[multiple][ng\\:model="$1"]', this.name);
41838 select.val(values);
41839 select.trigger('change');
41844 return function(name) {
41852 * element(selector, label).count() get the number of elements that match selector
41853 * element(selector, label).click() clicks an element
41854 * element(selector, label).mouseover() mouseover an element
41855 * element(selector, label).mousedown() mousedown an element
41856 * element(selector, label).mouseup() mouseup an element
41857 * element(selector, label).query(fn) executes fn(selectedElements, done)
41858 * element(selector, label).{method}() gets the value (as defined by jQuery, ex. val)
41859 * element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val)
41860 * element(selector, label).{method}(key) gets the value (as defined by jQuery, ex. attr)
41861 * element(selector, label).{method}(key, value) sets the value (as defined by jQuery, ex. attr)
41863 angular.scenario.dsl('element', function() {
41864 var KEY_VALUE_METHODS = ['attr', 'css', 'prop'];
41865 var VALUE_METHODS = [
41866 'val', 'text', 'html', 'height', 'innerHeight', 'outerHeight', 'width',
41867 'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset'
41871 chain.count = function() {
41872 return this.addFutureAction("element '" + this.label + "' count",
41873 function($window, $document, done) {
41875 done(null, $document.elements().length);
41882 chain.click = function() {
41883 return this.addFutureAction("element '" + this.label + "' click",
41884 function($window, $document, done) {
41885 var elements = $document.elements();
41886 var href = elements.attr('href');
41887 var eventProcessDefault = elements.trigger('click')[0];
41889 if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) {
41890 this.application.navigateTo(href, function() {
41899 chain.dblclick = function() {
41900 return this.addFutureAction("element '" + this.label + "' dblclick",
41901 function($window, $document, done) {
41902 var elements = $document.elements();
41903 var href = elements.attr('href');
41904 var eventProcessDefault = elements.trigger('dblclick')[0];
41906 if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) {
41907 this.application.navigateTo(href, function() {
41916 chain.mouseover = function() {
41917 return this.addFutureAction("element '" + this.label + "' mouseover",
41918 function($window, $document, done) {
41919 var elements = $document.elements();
41920 elements.trigger('mouseover');
41925 chain.mousedown = function() {
41926 return this.addFutureAction("element '" + this.label + "' mousedown",
41927 function($window, $document, done) {
41928 var elements = $document.elements();
41929 elements.trigger('mousedown');
41934 chain.mouseup = function() {
41935 return this.addFutureAction("element '" + this.label + "' mouseup",
41936 function($window, $document, done) {
41937 var elements = $document.elements();
41938 elements.trigger('mouseup');
41943 chain.query = function(fn) {
41944 return this.addFutureAction('element ' + this.label + ' custom query',
41945 function($window, $document, done) {
41946 fn.call(this, $document.elements(), done);
41950 angular.forEach(KEY_VALUE_METHODS, function(methodName) {
41951 chain[methodName] = function(name, value) {
41952 var args = arguments,
41953 futureName = (args.length == 1)
41954 ? "element '" + this.label + "' get " + methodName + " '" + name + "'"
41955 : "element '" + this.label + "' set " + methodName + " '" + name + "' to " + "'" +
41958 return this.addFutureAction(futureName, function($window, $document, done) {
41959 var element = $document.elements();
41960 done(null, element[methodName].apply(element, args));
41965 angular.forEach(VALUE_METHODS, function(methodName) {
41966 chain[methodName] = function(value) {
41967 var args = arguments,
41968 futureName = (args.length === 0)
41969 ? "element '" + this.label + "' " + methodName
41970 : "element '" + this.label + "' set " + methodName + " to '" + value + "'";
41972 return this.addFutureAction(futureName, function($window, $document, done) {
41973 var element = $document.elements();
41974 done(null, element[methodName].apply(element, args));
41979 return function(selector, label) {
41980 this.dsl.using(selector, label);
41986 * Matchers for implementing specs. Follows the Jasmine spec conventions.
41989 angular.scenario.matcher('toEqual', function(expected) {
41990 return angular.equals(this.actual, expected);
41993 angular.scenario.matcher('toBe', function(expected) {
41994 return this.actual === expected;
41997 angular.scenario.matcher('toBeDefined', function() {
41998 return angular.isDefined(this.actual);
42001 angular.scenario.matcher('toBeTruthy', function() {
42002 return this.actual;
42005 angular.scenario.matcher('toBeFalsy', function() {
42006 return !this.actual;
42009 angular.scenario.matcher('toMatch', function(expected) {
42010 return new RegExp(expected).test(this.actual);
42013 angular.scenario.matcher('toBeNull', function() {
42014 return this.actual === null;
42017 angular.scenario.matcher('toContain', function(expected) {
42018 return includes(this.actual, expected);
42021 angular.scenario.matcher('toBeLessThan', function(expected) {
42022 return this.actual < expected;
42025 angular.scenario.matcher('toBeGreaterThan', function(expected) {
42026 return this.actual > expected;
42030 * User Interface for the Scenario Runner.
42032 * TODO(esprehn): This should be refactored now that ObjectModel exists
42033 * to use angular bindings for the UI.
42035 angular.scenario.output('html', function(context, runner, model) {
42036 var specUiMap = {},
42037 lastStepUiMap = {};
42040 '<div id="header">' +
42041 ' <h1><span class="angular">AngularJS</span>: Scenario Test Runner</h1>' +
42042 ' <ul id="status-legend" class="status-display">' +
42043 ' <li class="status-error">0 Errors</li>' +
42044 ' <li class="status-failure">0 Failures</li>' +
42045 ' <li class="status-success">0 Passed</li>' +
42048 '<div id="specs">' +
42049 ' <div class="test-children"></div>' +
42053 runner.on('InteractivePause', function(spec) {
42054 var ui = lastStepUiMap[spec.id];
42055 ui.find('.test-title').
42056 html('paused... <a href="javascript:resume()">resume</a> when ready.');
42059 runner.on('SpecBegin', function(spec) {
42060 var ui = findContext(spec);
42061 ui.find('> .tests').append(
42062 '<li class="status-pending test-it"></li>'
42064 ui = ui.find('> .tests li:last');
42066 '<div class="test-info">' +
42067 ' <p class="test-title">' +
42068 ' <span class="timer-result"></span>' +
42069 ' <span class="test-name"></span>' +
42072 '<div class="scrollpane">' +
42073 ' <ol class="test-actions"></ol>' +
42076 ui.find('> .test-info .test-name').text(spec.name);
42077 ui.find('> .test-info').click(function() {
42078 var scrollpane = ui.find('> .scrollpane');
42079 var actions = scrollpane.find('> .test-actions');
42080 var name = context.find('> .test-info .test-name');
42081 if (actions.find(':visible').length) {
42083 name.removeClass('open').addClass('closed');
42086 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
42087 name.removeClass('closed').addClass('open');
42091 specUiMap[spec.id] = ui;
42094 runner.on('SpecError', function(spec, error) {
42095 var ui = specUiMap[spec.id];
42096 ui.append('<pre></pre>');
42097 ui.find('> pre').text(formatException(error));
42100 runner.on('SpecEnd', function(spec) {
42101 var ui = specUiMap[spec.id];
42102 spec = model.getSpec(spec.id);
42103 ui.removeClass('status-pending');
42104 ui.addClass('status-' + spec.status);
42105 ui.find("> .test-info .timer-result").text(spec.duration + "ms");
42106 if (spec.status === 'success') {
42107 ui.find('> .test-info .test-name').addClass('closed');
42108 ui.find('> .scrollpane .test-actions').hide();
42110 updateTotals(spec.status);
42113 runner.on('StepBegin', function(spec, step) {
42114 var ui = specUiMap[spec.id];
42115 spec = model.getSpec(spec.id);
42116 step = spec.getLastStep();
42117 ui.find('> .scrollpane .test-actions').append('<li class="status-pending"></li>');
42118 var stepUi = lastStepUiMap[spec.id] = ui.find('> .scrollpane .test-actions li:last');
42120 '<div class="timer-result"></div>' +
42121 '<div class="test-title"></div>'
42123 stepUi.find('> .test-title').text(step.name);
42124 var scrollpane = stepUi.parents('.scrollpane');
42125 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
42128 runner.on('StepFailure', function(spec, step, error) {
42129 var ui = lastStepUiMap[spec.id];
42130 addError(ui, step.line, error);
42133 runner.on('StepError', function(spec, step, error) {
42134 var ui = lastStepUiMap[spec.id];
42135 addError(ui, step.line, error);
42138 runner.on('StepEnd', function(spec, step) {
42139 var stepUi = lastStepUiMap[spec.id];
42140 spec = model.getSpec(spec.id);
42141 step = spec.getLastStep();
42142 stepUi.find('.timer-result').text(step.duration + 'ms');
42143 stepUi.removeClass('status-pending');
42144 stepUi.addClass('status-' + step.status);
42145 var scrollpane = specUiMap[spec.id].find('> .scrollpane');
42146 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
42150 * Finds the context of a spec block defined by the passed definition.
42152 * @param {Object} The definition created by the Describe object.
42154 function findContext(spec) {
42155 var currentContext = context.find('#specs');
42156 angular.forEach(model.getDefinitionPath(spec), function(defn) {
42157 var id = 'describe-' + defn.id;
42158 if (!context.find('#' + id).length) {
42159 currentContext.find('> .test-children').append(
42160 '<div class="test-describe" id="' + id + '">' +
42162 ' <div class="test-children"></div>' +
42163 ' <ul class="tests"></ul>' +
42166 context.find('#' + id).find('> h2').text('describe: ' + defn.name);
42168 currentContext = context.find('#' + id);
42170 return context.find('#describe-' + spec.definition.id);
42174 * Updates the test counter for the status.
42176 * @param {string} the status.
42178 function updateTotals(status) {
42179 var legend = context.find('#status-legend .status-' + status);
42180 var parts = legend.text().split(' ');
42181 var value = (parts[0] * 1) + 1;
42182 legend.text(value + ' ' + parts[1]);
42186 * Add an error to a step.
42188 * @param {Object} The JQuery wrapped context
42189 * @param {function()} fn() that should return the file/line number of the error
42190 * @param {Object} the error.
42192 function addError(context, line, error) {
42193 context.find('.test-title').append('<pre></pre>');
42194 var message = _jQuery.trim(line() + '\n\n' + formatException(error));
42195 context.find('.test-title pre:last').text(message);
42200 * Generates JSON output into a context.
42202 angular.scenario.output('json', function(context, runner, model) {
42203 model.on('RunnerEnd', function() {
42204 context.text(angular.toJson(model.value));
42209 * Generates XML output into a context.
42211 angular.scenario.output('xml', function(context, runner, model) {
42212 var $ = function(args) {return new context.init(args);};
42213 model.on('RunnerEnd', function() {
42214 var scenario = $('<scenario></scenario>');
42215 context.append(scenario);
42216 serializeXml(scenario, model.value);
42220 * Convert the tree into XML.
42222 * @param {Object} context jQuery context to add the XML to.
42223 * @param {Object} tree node to serialize
42225 function serializeXml(context, tree) {
42226 angular.forEach(tree.children, function(child) {
42227 var describeContext = $('<describe></describe>');
42228 describeContext.attr('id', child.id);
42229 describeContext.attr('name', child.name);
42230 context.append(describeContext);
42231 serializeXml(describeContext, child);
42233 var its = $('<its></its>');
42234 context.append(its);
42235 angular.forEach(tree.specs, function(spec) {
42236 var it = $('<it></it>');
42237 it.attr('id', spec.id);
42238 it.attr('name', spec.name);
42239 it.attr('duration', spec.duration);
42240 it.attr('status', spec.status);
42242 angular.forEach(spec.steps, function(step) {
42243 var stepContext = $('<step></step>');
42244 stepContext.attr('name', step.name);
42245 stepContext.attr('duration', step.duration);
42246 stepContext.attr('status', step.status);
42247 it.append(stepContext);
42249 var error = $('<error></error>');
42250 stepContext.append(error);
42251 error.text(formatException(step.error));
42259 * Creates a global value $result with the result of the runner.
42261 angular.scenario.output('object', function(context, runner, model) {
42262 runner.$window.$result = model.value;
42266 publishExternalAPI(angular);
42268 var $runner = new angular.scenario.Runner(window),
42269 scripts = window.document.getElementsByTagName('script'),
42270 script = scripts[scripts.length - 1],
42273 angular.forEach(script.attributes, function(attr) {
42274 var match = attr.name.match(/ng[:\-](.*)/);
42276 config[match[1]] = attr.value || true;
42280 if (config.autotest) {
42281 JQLite(window.document).ready(function() {
42282 angular.scenario.setUpAndRun(config);
42288 !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide:not(.ng-hide-animate) {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n\n.ng-animate-shim {\n visibility:hidden;\n}\n\n.ng-anchor {\n position:absolute;\n}\n</style>');
42289 !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";\n/* CSS Document */\n\n/** Structure */\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n font-size: 14px;\n}\n\n#system-error {\n font-size: 1.5em;\n text-align: center;\n}\n\n#json, #xml {\n display: none;\n}\n\n#header {\n position: fixed;\n width: 100%;\n}\n\n#specs {\n padding-top: 50px;\n}\n\n#header .angular {\n font-family: Courier New, monospace;\n font-weight: bold;\n}\n\n#header h1 {\n font-weight: normal;\n float: left;\n font-size: 30px;\n line-height: 30px;\n margin: 0;\n padding: 10px 10px;\n height: 30px;\n}\n\n#application h2,\n#specs h2 {\n margin: 0;\n padding: 0.5em;\n font-size: 1.1em;\n}\n\n#status-legend {\n margin-top: 10px;\n margin-right: 10px;\n}\n\n#header,\n#application,\n.test-info,\n.test-actions li {\n overflow: hidden;\n}\n\n#application {\n margin: 10px;\n}\n\n#application iframe {\n width: 100%;\n height: 758px;\n}\n\n#application .popout {\n float: right;\n}\n\n#application iframe {\n border: none;\n}\n\n.tests li,\n.test-actions li,\n.test-it li,\n.test-it ol,\n.status-display {\n list-style-type: none;\n}\n\n.tests,\n.test-it ol,\n.status-display {\n margin: 0;\n padding: 0;\n}\n\n.test-info {\n margin-left: 1em;\n margin-top: 0.5em;\n border-radius: 8px 0 0 8px;\n -webkit-border-radius: 8px 0 0 8px;\n -moz-border-radius: 8px 0 0 8px;\n cursor: pointer;\n}\n\n.test-info:hover .test-name {\n text-decoration: underline;\n}\n\n.test-info .closed:before {\n content: \'\\25b8\\00A0\';\n}\n\n.test-info .open:before {\n content: \'\\25be\\00A0\';\n font-weight: bold;\n}\n\n.test-it ol {\n margin-left: 2.5em;\n}\n\n.status-display,\n.status-display li {\n float: right;\n}\n\n.status-display li {\n padding: 5px 10px;\n}\n\n.timer-result,\n.test-title {\n display: inline-block;\n margin: 0;\n padding: 4px;\n}\n\n.test-actions .test-title,\n.test-actions .test-result {\n display: table-cell;\n padding-left: 0.5em;\n padding-right: 0.5em;\n}\n\n.test-actions {\n display: table;\n}\n\n.test-actions li {\n display: table-row;\n}\n\n.timer-result {\n width: 4em;\n padding: 0 10px;\n text-align: right;\n font-family: monospace;\n}\n\n.test-it pre,\n.test-actions pre {\n clear: left;\n color: black;\n margin-left: 6em;\n}\n\n.test-describe {\n padding-bottom: 0.5em;\n}\n\n.test-describe .test-describe {\n margin: 5px 5px 10px 2em;\n}\n\n.test-actions .status-pending .test-title:before {\n content: \'\\00bb\\00A0\';\n}\n\n.scrollpane {\n max-height: 20em;\n overflow: auto;\n}\n\n/** Colors */\n\n#header {\n background-color: #F2C200;\n}\n\n#specs h2 {\n border-top: 2px solid #BABAD1;\n}\n\n#specs h2,\n#application h2 {\n background-color: #efefef;\n}\n\n#application {\n border: 1px solid #BABAD1;\n}\n\n.test-describe .test-describe {\n border-left: 1px solid #BABAD1;\n border-right: 1px solid #BABAD1;\n border-bottom: 1px solid #BABAD1;\n}\n\n.status-display {\n border: 1px solid #777;\n}\n\n.status-display .status-pending,\n.status-pending .test-info {\n background-color: #F9EEBC;\n}\n\n.status-display .status-success,\n.status-success .test-info {\n background-color: #B1D7A1;\n}\n\n.status-display .status-failure,\n.status-failure .test-info {\n background-color: #FF8286;\n}\n\n.status-display .status-error,\n.status-error .test-info {\n background-color: black;\n color: white;\n}\n\n.test-actions .status-success .test-title {\n color: #30B30A;\n}\n\n.test-actions .status-failure .test-title {\n color: #DF0000;\n}\n\n.test-actions .status-error .test-title {\n color: black;\n}\n\n.test-actions .timer-result {\n color: #888;\n}\n</style>');