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.4.8
9194 * (c) 2010-2015 Google, Inc. http://angularjs.org
9197 (function(window, document){
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.4.8/' +
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 ////////////////////////////////////
9368 * # ng (core module)
9369 * The ng module is loaded by default when an AngularJS application is started. The module itself
9370 * contains the essential components for an AngularJS application to function. The table below
9371 * lists a high level breakdown of each of the services/factories, filters, directives and testing
9372 * components available within this core module.
9374 * <div doc-module-components="ng"></div>
9377 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
9379 // The name of a form control's ValidityState property.
9380 // This is used so that it's possible for internal tests to create mock ValidityStates.
9381 var VALIDITY_STATE_PROPERTY = 'validity';
9385 * @name angular.lowercase
9389 * @description Converts the specified string to lowercase.
9390 * @param {string} string String to be converted to lowercase.
9391 * @returns {string} Lowercased string.
9393 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
9394 var hasOwnProperty = Object.prototype.hasOwnProperty;
9398 * @name angular.uppercase
9402 * @description Converts the specified string to uppercase.
9403 * @param {string} string String to be converted to uppercase.
9404 * @returns {string} Uppercased string.
9406 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
9409 var manualLowercase = function(s) {
9410 /* jshint bitwise: false */
9412 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
9415 var manualUppercase = function(s) {
9416 /* jshint bitwise: false */
9418 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
9423 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
9424 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
9425 // with correct but slower alternatives.
9426 if ('i' !== 'I'.toLowerCase()) {
9427 lowercase = manualLowercase;
9428 uppercase = manualUppercase;
9433 msie, // holds major version number for IE, or NaN if UA is not IE.
9434 jqLite, // delay binding since jQuery could be loaded after us.
9435 jQuery, // delay binding
9439 toString = Object.prototype.toString,
9440 getPrototypeOf = Object.getPrototypeOf,
9441 ngMinErr = minErr('ng'),
9443 /** @name angular */
9444 angular = window.angular || (window.angular = {}),
9449 * documentMode is an IE-only property
9450 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
9452 msie = document.documentMode;
9458 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
9461 function isArrayLike(obj) {
9463 // `null`, `undefined` and `window` are not array-like
9464 if (obj == null || isWindow(obj)) return false;
9466 // arrays, strings and jQuery/jqLite objects are array like
9467 // * jqLite is either the jQuery or jqLite constructor function
9468 // * we have to check the existance of jqLite first as this method is called
9469 // via the forEach method when constructing the jqLite object in the first place
9470 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
9472 // Support: iOS 8.2 (not reproducible in simulator)
9473 // "length" in obj used to prevent JIT error (gh-11508)
9474 var length = "length" in Object(obj) && obj.length;
9476 // NodeList objects (with `item` method) and
9477 // other objects with suitable length characteristics are array-like
9478 return isNumber(length) &&
9479 (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
9484 * @name angular.forEach
9489 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
9490 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
9491 * is the value of an object property or an array element, `key` is the object property key or
9492 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
9494 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
9495 * using the `hasOwnProperty` method.
9498 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
9499 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
9500 * return the value provided.
9503 var values = {name: 'misko', gender: 'male'};
9505 angular.forEach(values, function(value, key) {
9506 this.push(key + ': ' + value);
9508 expect(log).toEqual(['name: misko', 'gender: male']);
9511 * @param {Object|Array} obj Object to iterate over.
9512 * @param {Function} iterator Iterator function.
9513 * @param {Object=} context Object to become context (`this`) for the iterator function.
9514 * @returns {Object|Array} Reference to `obj`.
9517 function forEach(obj, iterator, context) {
9520 if (isFunction(obj)) {
9522 // Need to check if hasOwnProperty exists,
9523 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
9524 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
9525 iterator.call(context, obj[key], key, obj);
9528 } else if (isArray(obj) || isArrayLike(obj)) {
9529 var isPrimitive = typeof obj !== 'object';
9530 for (key = 0, length = obj.length; key < length; key++) {
9531 if (isPrimitive || key in obj) {
9532 iterator.call(context, obj[key], key, obj);
9535 } else if (obj.forEach && obj.forEach !== forEach) {
9536 obj.forEach(iterator, context, obj);
9537 } else if (isBlankObject(obj)) {
9538 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
9540 iterator.call(context, obj[key], key, obj);
9542 } else if (typeof obj.hasOwnProperty === 'function') {
9543 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
9545 if (obj.hasOwnProperty(key)) {
9546 iterator.call(context, obj[key], key, obj);
9550 // Slow path for objects which do not have a method `hasOwnProperty`
9552 if (hasOwnProperty.call(obj, key)) {
9553 iterator.call(context, obj[key], key, obj);
9561 function forEachSorted(obj, iterator, context) {
9562 var keys = Object.keys(obj).sort();
9563 for (var i = 0; i < keys.length; i++) {
9564 iterator.call(context, obj[keys[i]], keys[i]);
9571 * when using forEach the params are value, key, but it is often useful to have key, value.
9572 * @param {function(string, *)} iteratorFn
9573 * @returns {function(*, string)}
9575 function reverseParams(iteratorFn) {
9576 return function(value, key) { iteratorFn(key, value); };
9580 * A consistent way of creating unique IDs in angular.
9582 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
9583 * we hit number precision issues in JavaScript.
9585 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
9587 * @returns {number} an unique alpha-numeric string
9589 function nextUid() {
9595 * Set or clear the hashkey for an object.
9597 * @param h the hashkey (!truthy to delete the hashkey)
9599 function setHashKey(obj, h) {
9603 delete obj.$$hashKey;
9608 function baseExtend(dst, objs, deep) {
9609 var h = dst.$$hashKey;
9611 for (var i = 0, ii = objs.length; i < ii; ++i) {
9613 if (!isObject(obj) && !isFunction(obj)) continue;
9614 var keys = Object.keys(obj);
9615 for (var j = 0, jj = keys.length; j < jj; j++) {
9619 if (deep && isObject(src)) {
9621 dst[key] = new Date(src.valueOf());
9622 } else if (isRegExp(src)) {
9623 dst[key] = new RegExp(src);
9624 } else if (src.nodeName) {
9625 dst[key] = src.cloneNode(true);
9626 } else if (isElement(src)) {
9627 dst[key] = src.clone();
9629 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
9630 baseExtend(dst[key], [src], true);
9644 * @name angular.extend
9649 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
9650 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
9651 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
9653 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
9654 * {@link angular.merge} for this.
9656 * @param {Object} dst Destination object.
9657 * @param {...Object} src Source object(s).
9658 * @returns {Object} Reference to `dst`.
9660 function extend(dst) {
9661 return baseExtend(dst, slice.call(arguments, 1), false);
9667 * @name angular.merge
9672 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
9673 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
9674 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
9676 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
9677 * objects, performing a deep copy.
9679 * @param {Object} dst Destination object.
9680 * @param {...Object} src Source object(s).
9681 * @returns {Object} Reference to `dst`.
9683 function merge(dst) {
9684 return baseExtend(dst, slice.call(arguments, 1), true);
9689 function toInt(str) {
9690 return parseInt(str, 10);
9694 function inherit(parent, extra) {
9695 return extend(Object.create(parent), extra);
9700 * @name angular.noop
9705 * A function that performs no operations. This function can be useful when writing code in the
9708 function foo(callback) {
9709 var result = calculateResult();
9710 (callback || angular.noop)(result);
9720 * @name angular.identity
9725 * A function that returns its first argument. This function is useful when writing code in the
9729 function transformer(transformationFn, value) {
9730 return (transformationFn || angular.identity)(value);
9733 * @param {*} value to be returned.
9734 * @returns {*} the value passed in.
9736 function identity($) {return $;}
9737 identity.$inject = [];
9740 function valueFn(value) {return function() {return value;};}
9742 function hasCustomToString(obj) {
9743 return isFunction(obj.toString) && obj.toString !== toString;
9749 * @name angular.isUndefined
9754 * Determines if a reference is undefined.
9756 * @param {*} value Reference to check.
9757 * @returns {boolean} True if `value` is undefined.
9759 function isUndefined(value) {return typeof value === 'undefined';}
9764 * @name angular.isDefined
9769 * Determines if a reference is defined.
9771 * @param {*} value Reference to check.
9772 * @returns {boolean} True if `value` is defined.
9774 function isDefined(value) {return typeof value !== 'undefined';}
9779 * @name angular.isObject
9784 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
9785 * considered to be objects. Note that JavaScript arrays are objects.
9787 * @param {*} value Reference to check.
9788 * @returns {boolean} True if `value` is an `Object` but not `null`.
9790 function isObject(value) {
9791 // http://jsperf.com/isobject4
9792 return value !== null && typeof value === 'object';
9797 * Determine if a value is an object with a null prototype
9799 * @returns {boolean} True if `value` is an `Object` with a null prototype
9801 function isBlankObject(value) {
9802 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
9808 * @name angular.isString
9813 * Determines if a reference is a `String`.
9815 * @param {*} value Reference to check.
9816 * @returns {boolean} True if `value` is a `String`.
9818 function isString(value) {return typeof value === 'string';}
9823 * @name angular.isNumber
9828 * Determines if a reference is a `Number`.
9830 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
9832 * If you wish to exclude these then you can use the native
9833 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
9836 * @param {*} value Reference to check.
9837 * @returns {boolean} True if `value` is a `Number`.
9839 function isNumber(value) {return typeof value === 'number';}
9844 * @name angular.isDate
9849 * Determines if a value is a date.
9851 * @param {*} value Reference to check.
9852 * @returns {boolean} True if `value` is a `Date`.
9854 function isDate(value) {
9855 return toString.call(value) === '[object Date]';
9861 * @name angular.isArray
9866 * Determines if a reference is an `Array`.
9868 * @param {*} value Reference to check.
9869 * @returns {boolean} True if `value` is an `Array`.
9871 var isArray = Array.isArray;
9875 * @name angular.isFunction
9880 * Determines if a reference is a `Function`.
9882 * @param {*} value Reference to check.
9883 * @returns {boolean} True if `value` is a `Function`.
9885 function isFunction(value) {return typeof value === 'function';}
9889 * Determines if a value is a regular expression object.
9892 * @param {*} value Reference to check.
9893 * @returns {boolean} True if `value` is a `RegExp`.
9895 function isRegExp(value) {
9896 return toString.call(value) === '[object RegExp]';
9901 * Checks if `obj` is a window object.
9904 * @param {*} obj Object to check
9905 * @returns {boolean} True if `obj` is a window obj.
9907 function isWindow(obj) {
9908 return obj && obj.window === obj;
9912 function isScope(obj) {
9913 return obj && obj.$evalAsync && obj.$watch;
9917 function isFile(obj) {
9918 return toString.call(obj) === '[object File]';
9922 function isFormData(obj) {
9923 return toString.call(obj) === '[object FormData]';
9927 function isBlob(obj) {
9928 return toString.call(obj) === '[object Blob]';
9932 function isBoolean(value) {
9933 return typeof value === 'boolean';
9937 function isPromiseLike(obj) {
9938 return obj && isFunction(obj.then);
9942 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
9943 function isTypedArray(value) {
9944 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
9948 var trim = function(value) {
9949 return isString(value) ? value.trim() : value;
9953 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
9954 // Prereq: s is a string.
9955 var escapeForRegexp = function(s) {
9956 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
9957 replace(/\x08/g, '\\x08');
9963 * @name angular.isElement
9968 * Determines if a reference is a DOM element (or wrapped jQuery element).
9970 * @param {*} value Reference to check.
9971 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
9973 function isElement(node) {
9975 (node.nodeName // we are a direct element
9976 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
9980 * @param str 'key1,key2,...'
9981 * @returns {object} in the form of {key1:true, key2:true, ...}
9983 function makeMap(str) {
9984 var obj = {}, items = str.split(","), i;
9985 for (i = 0; i < items.length; i++) {
9986 obj[items[i]] = true;
9992 function nodeName_(element) {
9993 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
9996 function includes(array, obj) {
9997 return Array.prototype.indexOf.call(array, obj) != -1;
10000 function arrayRemove(array, value) {
10001 var index = array.indexOf(value);
10003 array.splice(index, 1);
10010 * @name angular.copy
10015 * Creates a deep copy of `source`, which should be an object or an array.
10017 * * If no destination is supplied, a copy of the object or array is created.
10018 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
10019 * are deleted and then all elements/properties from the source are copied to it.
10020 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
10021 * * If `source` is identical to 'destination' an exception will be thrown.
10023 * @param {*} source The source that will be used to make a copy.
10024 * Can be any type, including primitives, `null`, and `undefined`.
10025 * @param {(Object|Array)=} destination Destination into which the source is copied. If
10026 * provided, must be of the same type as `source`.
10027 * @returns {*} The copy or updated `destination`, if `destination` was specified.
10030 <example module="copyExample">
10031 <file name="index.html">
10032 <div ng-controller="ExampleController">
10033 <form novalidate class="simple-form">
10034 Name: <input type="text" ng-model="user.name" /><br />
10035 E-mail: <input type="email" ng-model="user.email" /><br />
10036 Gender: <input type="radio" ng-model="user.gender" value="male" />male
10037 <input type="radio" ng-model="user.gender" value="female" />female<br />
10038 <button ng-click="reset()">RESET</button>
10039 <button ng-click="update(user)">SAVE</button>
10041 <pre>form = {{user | json}}</pre>
10042 <pre>master = {{master | json}}</pre>
10046 angular.module('copyExample', [])
10047 .controller('ExampleController', ['$scope', function($scope) {
10050 $scope.update = function(user) {
10051 // Example with 1 argument
10052 $scope.master= angular.copy(user);
10055 $scope.reset = function() {
10056 // Example with 2 arguments
10057 angular.copy($scope.master, $scope.user);
10066 function copy(source, destination) {
10067 var stackSource = [];
10068 var stackDest = [];
10071 if (isTypedArray(destination)) {
10072 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
10074 if (source === destination) {
10075 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
10078 // Empty the destination object
10079 if (isArray(destination)) {
10080 destination.length = 0;
10082 forEach(destination, function(value, key) {
10083 if (key !== '$$hashKey') {
10084 delete destination[key];
10089 stackSource.push(source);
10090 stackDest.push(destination);
10091 return copyRecurse(source, destination);
10094 return copyElement(source);
10096 function copyRecurse(source, destination) {
10097 var h = destination.$$hashKey;
10099 if (isArray(source)) {
10100 for (var i = 0, ii = source.length; i < ii; i++) {
10101 destination.push(copyElement(source[i]));
10103 } else if (isBlankObject(source)) {
10104 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
10105 for (key in source) {
10106 destination[key] = copyElement(source[key]);
10108 } else if (source && typeof source.hasOwnProperty === 'function') {
10109 // Slow path, which must rely on hasOwnProperty
10110 for (key in source) {
10111 if (source.hasOwnProperty(key)) {
10112 destination[key] = copyElement(source[key]);
10116 // Slowest path --- hasOwnProperty can't be called as a method
10117 for (key in source) {
10118 if (hasOwnProperty.call(source, key)) {
10119 destination[key] = copyElement(source[key]);
10123 setHashKey(destination, h);
10124 return destination;
10127 function copyElement(source) {
10129 if (!isObject(source)) {
10133 // Already copied values
10134 var index = stackSource.indexOf(source);
10135 if (index !== -1) {
10136 return stackDest[index];
10139 if (isWindow(source) || isScope(source)) {
10140 throw ngMinErr('cpws',
10141 "Can't copy! Making copies of Window or Scope instances is not supported.");
10144 var needsRecurse = false;
10147 if (isArray(source)) {
10149 needsRecurse = true;
10150 } else if (isTypedArray(source)) {
10151 destination = new source.constructor(source);
10152 } else if (isDate(source)) {
10153 destination = new Date(source.getTime());
10154 } else if (isRegExp(source)) {
10155 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
10156 destination.lastIndex = source.lastIndex;
10157 } else if (isFunction(source.cloneNode)) {
10158 destination = source.cloneNode(true);
10160 destination = Object.create(getPrototypeOf(source));
10161 needsRecurse = true;
10164 stackSource.push(source);
10165 stackDest.push(destination);
10167 return needsRecurse
10168 ? copyRecurse(source, destination)
10174 * Creates a shallow copy of an object, an array or a primitive.
10176 * Assumes that there are no proto properties for objects.
10178 function shallowCopy(src, dst) {
10179 if (isArray(src)) {
10182 for (var i = 0, ii = src.length; i < ii; i++) {
10185 } else if (isObject(src)) {
10188 for (var key in src) {
10189 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
10190 dst[key] = src[key];
10201 * @name angular.equals
10206 * Determines if two objects or two values are equivalent. Supports value types, regular
10207 * expressions, arrays and objects.
10209 * Two objects or values are considered equivalent if at least one of the following is true:
10211 * * Both objects or values pass `===` comparison.
10212 * * Both objects or values are of the same type and all of their properties are equal by
10213 * comparing them with `angular.equals`.
10214 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
10215 * * Both values represent the same regular expression (In JavaScript,
10216 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
10217 * representation matches).
10219 * During a property comparison, properties of `function` type and properties with names
10220 * that begin with `$` are ignored.
10222 * Scope and DOMWindow objects are being compared only by identify (`===`).
10224 * @param {*} o1 Object or value to compare.
10225 * @param {*} o2 Object or value to compare.
10226 * @returns {boolean} True if arguments are equal.
10228 function equals(o1, o2) {
10229 if (o1 === o2) return true;
10230 if (o1 === null || o2 === null) return false;
10231 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
10232 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
10234 if (t1 == 'object') {
10236 if (!isArray(o2)) return false;
10237 if ((length = o1.length) == o2.length) {
10238 for (key = 0; key < length; key++) {
10239 if (!equals(o1[key], o2[key])) return false;
10243 } else if (isDate(o1)) {
10244 if (!isDate(o2)) return false;
10245 return equals(o1.getTime(), o2.getTime());
10246 } else if (isRegExp(o1)) {
10247 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
10249 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
10250 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
10251 keySet = createMap();
10253 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
10254 if (!equals(o1[key], o2[key])) return false;
10255 keySet[key] = true;
10258 if (!(key in keySet) &&
10259 key.charAt(0) !== '$' &&
10260 isDefined(o2[key]) &&
10261 !isFunction(o2[key])) return false;
10270 var csp = function() {
10271 if (!isDefined(csp.rules)) {
10274 var ngCspElement = (document.querySelector('[ng-csp]') ||
10275 document.querySelector('[data-ng-csp]'));
10277 if (ngCspElement) {
10278 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
10279 ngCspElement.getAttribute('data-ng-csp');
10281 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
10282 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
10286 noUnsafeEval: noUnsafeEval(),
10287 noInlineStyle: false
10294 function noUnsafeEval() {
10296 /* jshint -W031, -W054 */
10298 /* jshint +W031, +W054 */
10312 * @param {string=} ngJq the name of the library available under `window`
10313 * to be used for angular.element
10315 * Use this directive to force the angular.element library. This should be
10316 * used to force either jqLite by leaving ng-jq blank or setting the name of
10317 * the jquery variable under window (eg. jQuery).
10319 * Since angular looks for this directive when it is loaded (doesn't wait for the
10320 * DOMContentLoaded event), it must be placed on an element that comes before the script
10321 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
10325 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
10328 <html ng-app ng-jq>
10334 * This example shows how to use a jQuery based library of a different name.
10335 * The library name must be available at the top most 'window'.
10338 <html ng-app ng-jq="jQueryLib">
10344 var jq = function() {
10345 if (isDefined(jq.name_)) return jq.name_;
10347 var i, ii = ngAttrPrefixes.length, prefix, name;
10348 for (i = 0; i < ii; ++i) {
10349 prefix = ngAttrPrefixes[i];
10350 if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
10351 name = el.getAttribute(prefix + 'jq');
10356 return (jq.name_ = name);
10359 function concat(array1, array2, index) {
10360 return array1.concat(slice.call(array2, index));
10363 function sliceArgs(args, startIndex) {
10364 return slice.call(args, startIndex || 0);
10371 * @name angular.bind
10376 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
10377 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
10378 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
10379 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
10381 * @param {Object} self Context which `fn` should be evaluated in.
10382 * @param {function()} fn Function to be bound.
10383 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
10384 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
10387 function bind(self, fn) {
10388 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
10389 if (isFunction(fn) && !(fn instanceof RegExp)) {
10390 return curryArgs.length
10392 return arguments.length
10393 ? fn.apply(self, concat(curryArgs, arguments, 0))
10394 : fn.apply(self, curryArgs);
10397 return arguments.length
10398 ? fn.apply(self, arguments)
10402 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
10408 function toJsonReplacer(key, value) {
10411 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
10413 } else if (isWindow(value)) {
10415 } else if (value && document === value) {
10417 } else if (isScope(value)) {
10427 * @name angular.toJson
10432 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
10433 * stripped since angular uses this notation internally.
10435 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
10436 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
10437 * If set to an integer, the JSON output will contain that many spaces per indentation.
10438 * @returns {string|undefined} JSON-ified string representing `obj`.
10440 function toJson(obj, pretty) {
10441 if (typeof obj === 'undefined') return undefined;
10442 if (!isNumber(pretty)) {
10443 pretty = pretty ? 2 : null;
10445 return JSON.stringify(obj, toJsonReplacer, pretty);
10451 * @name angular.fromJson
10456 * Deserializes a JSON string.
10458 * @param {string} json JSON string to deserialize.
10459 * @returns {Object|Array|string|number} Deserialized JSON string.
10461 function fromJson(json) {
10462 return isString(json)
10468 function timezoneToOffset(timezone, fallback) {
10469 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
10470 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
10474 function addDateMinutes(date, minutes) {
10475 date = new Date(date.getTime());
10476 date.setMinutes(date.getMinutes() + minutes);
10481 function convertTimezoneToLocal(date, timezone, reverse) {
10482 reverse = reverse ? -1 : 1;
10483 var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
10484 return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
10489 * @returns {string} Returns the string representation of the element.
10491 function startingTag(element) {
10492 element = jqLite(element).clone();
10494 // turns out IE does not let you set .html() on elements which
10495 // are not allowed to have children. So we just ignore it.
10498 var elemHtml = jqLite('<div>').append(element).html();
10500 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
10502 match(/^(<[^>]+>)/)[1].
10503 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
10505 return lowercase(elemHtml);
10511 /////////////////////////////////////////////////
10514 * Tries to decode the URI component without throwing an exception.
10517 * @param str value potential URI component to check.
10518 * @returns {boolean} True if `value` can be decoded
10519 * with the decodeURIComponent function.
10521 function tryDecodeURIComponent(value) {
10523 return decodeURIComponent(value);
10525 // Ignore any invalid uri component
10531 * Parses an escaped url query string into key-value pairs.
10532 * @returns {Object.<string,boolean|Array>}
10534 function parseKeyValue(/**string*/keyValue) {
10536 forEach((keyValue || "").split('&'), function(keyValue) {
10537 var splitPoint, key, val;
10539 key = keyValue = keyValue.replace(/\+/g,'%20');
10540 splitPoint = keyValue.indexOf('=');
10541 if (splitPoint !== -1) {
10542 key = keyValue.substring(0, splitPoint);
10543 val = keyValue.substring(splitPoint + 1);
10545 key = tryDecodeURIComponent(key);
10546 if (isDefined(key)) {
10547 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
10548 if (!hasOwnProperty.call(obj, key)) {
10550 } else if (isArray(obj[key])) {
10551 obj[key].push(val);
10553 obj[key] = [obj[key],val];
10561 function toKeyValue(obj) {
10563 forEach(obj, function(value, key) {
10564 if (isArray(value)) {
10565 forEach(value, function(arrayValue) {
10566 parts.push(encodeUriQuery(key, true) +
10567 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
10570 parts.push(encodeUriQuery(key, true) +
10571 (value === true ? '' : '=' + encodeUriQuery(value, true)));
10574 return parts.length ? parts.join('&') : '';
10579 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
10580 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
10583 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
10584 * pct-encoded = "%" HEXDIG HEXDIG
10585 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
10586 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
10587 * / "*" / "+" / "," / ";" / "="
10589 function encodeUriSegment(val) {
10590 return encodeUriQuery(val, true).
10591 replace(/%26/gi, '&').
10592 replace(/%3D/gi, '=').
10593 replace(/%2B/gi, '+');
10598 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
10599 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
10600 * encoded per http://tools.ietf.org/html/rfc3986:
10601 * query = *( pchar / "/" / "?" )
10602 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
10603 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
10604 * pct-encoded = "%" HEXDIG HEXDIG
10605 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
10606 * / "*" / "+" / "," / ";" / "="
10608 function encodeUriQuery(val, pctEncodeSpaces) {
10609 return encodeURIComponent(val).
10610 replace(/%40/gi, '@').
10611 replace(/%3A/gi, ':').
10612 replace(/%24/g, '$').
10613 replace(/%2C/gi, ',').
10614 replace(/%3B/gi, ';').
10615 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
10618 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
10620 function getNgAttribute(element, ngAttr) {
10621 var attr, i, ii = ngAttrPrefixes.length;
10622 for (i = 0; i < ii; ++i) {
10623 attr = ngAttrPrefixes[i] + ngAttr;
10624 if (isString(attr = element.getAttribute(attr))) {
10637 * @param {angular.Module} ngApp an optional application
10638 * {@link angular.module module} name to load.
10639 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
10640 * created in "strict-di" mode. This means that the application will fail to invoke functions which
10641 * do not use explicit function annotation (and are thus unsuitable for minification), as described
10642 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
10643 * tracking down the root of these bugs.
10647 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
10648 * designates the **root element** of the application and is typically placed near the root element
10649 * of the page - e.g. on the `<body>` or `<html>` tags.
10651 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
10652 * found in the document will be used to define the root element to auto-bootstrap as an
10653 * application. To run multiple applications in an HTML document you must manually bootstrap them using
10654 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
10656 * You can specify an **AngularJS module** to be used as the root module for the application. This
10657 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
10658 * should contain the application code needed or have dependencies on other modules that will
10659 * contain the code. See {@link angular.module} for more information.
10661 * In the example below if the `ngApp` directive were not placed on the `html` element then the
10662 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
10663 * would not be resolved to `3`.
10665 * `ngApp` is the easiest, and most common way to bootstrap an application.
10667 <example module="ngAppDemo">
10668 <file name="index.html">
10669 <div ng-controller="ngAppDemoController">
10670 I can add: {{a}} + {{b}} = {{ a+b }}
10673 <file name="script.js">
10674 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
10681 * Using `ngStrictDi`, you would see something like this:
10683 <example ng-app-included="true">
10684 <file name="index.html">
10685 <div ng-app="ngAppStrictDemo" ng-strict-di>
10686 <div ng-controller="GoodController1">
10687 I can add: {{a}} + {{b}} = {{ a+b }}
10689 <p>This renders because the controller does not fail to
10690 instantiate, by using explicit annotation style (see
10691 script.js for details)
10695 <div ng-controller="GoodController2">
10696 Name: <input ng-model="name"><br />
10699 <p>This renders because the controller does not fail to
10700 instantiate, by using explicit annotation style
10701 (see script.js for details)
10705 <div ng-controller="BadController">
10706 I can add: {{a}} + {{b}} = {{ a+b }}
10708 <p>The controller could not be instantiated, due to relying
10709 on automatic function annotations (which are disabled in
10710 strict mode). As such, the content of this section is not
10711 interpolated, and there should be an error in your web console.
10716 <file name="script.js">
10717 angular.module('ngAppStrictDemo', [])
10718 // BadController will fail to instantiate, due to relying on automatic function annotation,
10719 // rather than an explicit annotation
10720 .controller('BadController', function($scope) {
10724 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
10725 // due to using explicit annotations using the array style and $inject property, respectively.
10726 .controller('GoodController1', ['$scope', function($scope) {
10730 .controller('GoodController2', GoodController2);
10731 function GoodController2($scope) {
10732 $scope.name = "World";
10734 GoodController2.$inject = ['$scope'];
10736 <file name="style.css">
10737 div[ng-controller] {
10738 margin-bottom: 1em;
10739 -webkit-border-radius: 4px;
10740 border-radius: 4px;
10744 div[ng-controller^=Good] {
10745 border-color: #d6e9c6;
10746 background-color: #dff0d8;
10749 div[ng-controller^=Bad] {
10750 border-color: #ebccd1;
10751 background-color: #f2dede;
10758 function angularInit(element, bootstrap) {
10763 // The element `element` has priority over any other element
10764 forEach(ngAttrPrefixes, function(prefix) {
10765 var name = prefix + 'app';
10767 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
10768 appElement = element;
10769 module = element.getAttribute(name);
10772 forEach(ngAttrPrefixes, function(prefix) {
10773 var name = prefix + 'app';
10776 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
10777 appElement = candidate;
10778 module = candidate.getAttribute(name);
10782 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
10783 bootstrap(appElement, module ? [module] : [], config);
10789 * @name angular.bootstrap
10792 * Use this function to manually start up angular application.
10794 * See: {@link guide/bootstrap Bootstrap}
10796 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
10797 * They must use {@link ng.directive:ngApp ngApp}.
10799 * Angular will detect if it has been loaded into the browser more than once and only allow the
10800 * first loaded script to be bootstrapped and will report a warning to the browser console for
10801 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
10802 * multiple instances of Angular try to work on the DOM.
10808 * <div ng-controller="WelcomeController">
10812 * <script src="angular.js"></script>
10814 * var app = angular.module('demo', [])
10815 * .controller('WelcomeController', function($scope) {
10816 * $scope.greeting = 'Welcome!';
10818 * angular.bootstrap(document, ['demo']);
10824 * @param {DOMElement} element DOM element which is the root of angular application.
10825 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
10826 * Each item in the array should be the name of a predefined module or a (DI annotated)
10827 * function that will be invoked by the injector as a `config` block.
10828 * See: {@link angular.module modules}
10829 * @param {Object=} config an object for defining configuration options for the application. The
10830 * following keys are supported:
10832 * * `strictDi` - disable automatic function annotation for the application. This is meant to
10833 * assist in finding bugs which break minified code. Defaults to `false`.
10835 * @returns {auto.$injector} Returns the newly created injector for this app.
10837 function bootstrap(element, modules, config) {
10838 if (!isObject(config)) config = {};
10839 var defaultConfig = {
10842 config = extend(defaultConfig, config);
10843 var doBootstrap = function() {
10844 element = jqLite(element);
10846 if (element.injector()) {
10847 var tag = (element[0] === document) ? 'document' : startingTag(element);
10848 //Encode angle brackets to prevent input from being sanitized to empty string #8683
10851 "App Already Bootstrapped with this Element '{0}'",
10852 tag.replace(/</,'<').replace(/>/,'>'));
10855 modules = modules || [];
10856 modules.unshift(['$provide', function($provide) {
10857 $provide.value('$rootElement', element);
10860 if (config.debugInfoEnabled) {
10861 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
10862 modules.push(['$compileProvider', function($compileProvider) {
10863 $compileProvider.debugInfoEnabled(true);
10867 modules.unshift('ng');
10868 var injector = createInjector(modules, config.strictDi);
10869 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
10870 function bootstrapApply(scope, element, compile, injector) {
10871 scope.$apply(function() {
10872 element.data('$injector', injector);
10873 compile(element)(scope);
10880 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
10881 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
10883 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
10884 config.debugInfoEnabled = true;
10885 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
10888 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
10889 return doBootstrap();
10892 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
10893 angular.resumeBootstrap = function(extraModules) {
10894 forEach(extraModules, function(module) {
10895 modules.push(module);
10897 return doBootstrap();
10900 if (isFunction(angular.resumeDeferredBootstrap)) {
10901 angular.resumeDeferredBootstrap();
10907 * @name angular.reloadWithDebugInfo
10910 * Use this function to reload the current application with debug information turned on.
10911 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
10913 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
10915 function reloadWithDebugInfo() {
10916 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
10917 window.location.reload();
10921 * @name angular.getTestability
10924 * Get the testability service for the instance of Angular on the given
10926 * @param {DOMElement} element DOM element which is the root of angular application.
10928 function getTestability(rootElement) {
10929 var injector = angular.element(rootElement).injector();
10931 throw ngMinErr('test',
10932 'no injector found for element argument to getTestability');
10934 return injector.get('$$testability');
10937 var SNAKE_CASE_REGEXP = /[A-Z]/g;
10938 function snake_case(name, separator) {
10939 separator = separator || '_';
10940 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
10941 return (pos ? separator : '') + letter.toLowerCase();
10945 var bindJQueryFired = false;
10946 var skipDestroyOnNextJQueryCleanData;
10947 function bindJQuery() {
10948 var originalCleanData;
10950 if (bindJQueryFired) {
10954 // bind to jQuery if present;
10956 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
10957 !jqName ? undefined : // use jqLite
10958 window[jqName]; // use jQuery specified by `ngJq`
10960 // Use jQuery if it exists with proper functionality, otherwise default to us.
10961 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
10962 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
10963 // versions. It will not work for sure with jQuery <1.7, though.
10964 if (jQuery && jQuery.fn.on) {
10966 extend(jQuery.fn, {
10967 scope: JQLitePrototype.scope,
10968 isolateScope: JQLitePrototype.isolateScope,
10969 controller: JQLitePrototype.controller,
10970 injector: JQLitePrototype.injector,
10971 inheritedData: JQLitePrototype.inheritedData
10974 // All nodes removed from the DOM via various jQuery APIs like .remove()
10975 // are passed through jQuery.cleanData. Monkey-patch this method to fire
10976 // the $destroy event on all removed nodes.
10977 originalCleanData = jQuery.cleanData;
10978 jQuery.cleanData = function(elems) {
10980 if (!skipDestroyOnNextJQueryCleanData) {
10981 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
10982 events = jQuery._data(elem, "events");
10983 if (events && events.$destroy) {
10984 jQuery(elem).triggerHandler('$destroy');
10988 skipDestroyOnNextJQueryCleanData = false;
10990 originalCleanData(elems);
10996 angular.element = jqLite;
10998 // Prevent double-proxying.
10999 bindJQueryFired = true;
11003 * throw error if the argument is falsy.
11005 function assertArg(arg, name, reason) {
11007 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
11012 function assertArgFn(arg, name, acceptArrayAnnotation) {
11013 if (acceptArrayAnnotation && isArray(arg)) {
11014 arg = arg[arg.length - 1];
11017 assertArg(isFunction(arg), name, 'not a function, got ' +
11018 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
11023 * throw error if the name given is hasOwnProperty
11024 * @param {String} name the name to test
11025 * @param {String} context the context in which the name is used, such as module or directive
11027 function assertNotHasOwnProperty(name, context) {
11028 if (name === 'hasOwnProperty') {
11029 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
11034 * Return the value accessible from the object by path. Any undefined traversals are ignored
11035 * @param {Object} obj starting object
11036 * @param {String} path path to traverse
11037 * @param {boolean} [bindFnToScope=true]
11038 * @returns {Object} value as accessible by path
11040 //TODO(misko): this function needs to be removed
11041 function getter(obj, path, bindFnToScope) {
11042 if (!path) return obj;
11043 var keys = path.split('.');
11045 var lastInstance = obj;
11046 var len = keys.length;
11048 for (var i = 0; i < len; i++) {
11051 obj = (lastInstance = obj)[key];
11054 if (!bindFnToScope && isFunction(obj)) {
11055 return bind(lastInstance, obj);
11061 * Return the DOM siblings between the first and last node in the given array.
11062 * @param {Array} array like object
11063 * @returns {Array} the inputted object or a jqLite collection containing the nodes
11065 function getBlockNodes(nodes) {
11066 // TODO(perf): update `nodes` instead of creating a new object?
11067 var node = nodes[0];
11068 var endNode = nodes[nodes.length - 1];
11071 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
11072 if (blockNodes || nodes[i] !== node) {
11074 blockNodes = jqLite(slice.call(nodes, 0, i));
11076 blockNodes.push(node);
11080 return blockNodes || nodes;
11085 * Creates a new object without a prototype. This object is useful for lookup without having to
11086 * guard against prototypically inherited properties via hasOwnProperty.
11088 * Related micro-benchmarks:
11089 * - http://jsperf.com/object-create2
11090 * - http://jsperf.com/proto-map-lookup/2
11091 * - http://jsperf.com/for-in-vs-object-keys2
11093 * @returns {Object}
11095 function createMap() {
11096 return Object.create(null);
11099 var NODE_TYPE_ELEMENT = 1;
11100 var NODE_TYPE_ATTRIBUTE = 2;
11101 var NODE_TYPE_TEXT = 3;
11102 var NODE_TYPE_COMMENT = 8;
11103 var NODE_TYPE_DOCUMENT = 9;
11104 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
11108 * @name angular.Module
11112 * Interface for configuring angular {@link angular.module modules}.
11115 function setupModuleLoader(window) {
11117 var $injectorMinErr = minErr('$injector');
11118 var ngMinErr = minErr('ng');
11120 function ensure(obj, name, factory) {
11121 return obj[name] || (obj[name] = factory());
11124 var angular = ensure(window, 'angular', Object);
11126 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
11127 angular.$$minErr = angular.$$minErr || minErr;
11129 return ensure(angular, 'module', function() {
11130 /** @type {Object.<string, angular.Module>} */
11135 * @name angular.module
11139 * The `angular.module` is a global place for creating, registering and retrieving Angular
11141 * All modules (angular core or 3rd party) that should be available to an application must be
11142 * registered using this mechanism.
11144 * Passing one argument retrieves an existing {@link angular.Module},
11145 * whereas passing more than one argument creates a new {@link angular.Module}
11150 * A module is a collection of services, directives, controllers, filters, and configuration information.
11151 * `angular.module` is used to configure the {@link auto.$injector $injector}.
11154 * // Create a new module
11155 * var myModule = angular.module('myModule', []);
11157 * // register a new service
11158 * myModule.value('appName', 'MyCoolApp');
11160 * // configure existing services inside initialization blocks.
11161 * myModule.config(['$locationProvider', function($locationProvider) {
11162 * // Configure existing providers
11163 * $locationProvider.hashPrefix('!');
11167 * Then you can create an injector and load your modules like this:
11170 * var injector = angular.injector(['ng', 'myModule'])
11173 * However it's more likely that you'll just use
11174 * {@link ng.directive:ngApp ngApp} or
11175 * {@link angular.bootstrap} to simplify this process for you.
11177 * @param {!string} name The name of the module to create or retrieve.
11178 * @param {!Array.<string>=} requires If specified then new module is being created. If
11179 * unspecified then the module is being retrieved for further configuration.
11180 * @param {Function=} configFn Optional configuration function for the module. Same as
11181 * {@link angular.Module#config Module#config()}.
11182 * @returns {module} new module with the {@link angular.Module} api.
11184 return function module(name, requires, configFn) {
11185 var assertNotHasOwnProperty = function(name, context) {
11186 if (name === 'hasOwnProperty') {
11187 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
11191 assertNotHasOwnProperty(name, 'module');
11192 if (requires && modules.hasOwnProperty(name)) {
11193 modules[name] = null;
11195 return ensure(modules, name, function() {
11197 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
11198 "the module name or forgot to load it. If registering a module ensure that you " +
11199 "specify the dependencies as the second argument.", name);
11202 /** @type {!Array.<Array.<*>>} */
11203 var invokeQueue = [];
11205 /** @type {!Array.<Function>} */
11206 var configBlocks = [];
11208 /** @type {!Array.<Function>} */
11209 var runBlocks = [];
11211 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
11213 /** @type {angular.Module} */
11214 var moduleInstance = {
11216 _invokeQueue: invokeQueue,
11217 _configBlocks: configBlocks,
11218 _runBlocks: runBlocks,
11222 * @name angular.Module#requires
11226 * Holds the list of modules which the injector will load before the current module is
11229 requires: requires,
11233 * @name angular.Module#name
11237 * Name of the module.
11244 * @name angular.Module#provider
11246 * @param {string} name service name
11247 * @param {Function} providerType Construction function for creating new instance of the
11250 * See {@link auto.$provide#provider $provide.provider()}.
11252 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
11256 * @name angular.Module#factory
11258 * @param {string} name service name
11259 * @param {Function} providerFunction Function for creating new instance of the service.
11261 * See {@link auto.$provide#factory $provide.factory()}.
11263 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
11267 * @name angular.Module#service
11269 * @param {string} name service name
11270 * @param {Function} constructor A constructor function that will be instantiated.
11272 * See {@link auto.$provide#service $provide.service()}.
11274 service: invokeLaterAndSetModuleName('$provide', 'service'),
11278 * @name angular.Module#value
11280 * @param {string} name service name
11281 * @param {*} object Service instance object.
11283 * See {@link auto.$provide#value $provide.value()}.
11285 value: invokeLater('$provide', 'value'),
11289 * @name angular.Module#constant
11291 * @param {string} name constant name
11292 * @param {*} object Constant value.
11294 * Because the constants are fixed, they get applied before other provide methods.
11295 * See {@link auto.$provide#constant $provide.constant()}.
11297 constant: invokeLater('$provide', 'constant', 'unshift'),
11301 * @name angular.Module#decorator
11303 * @param {string} The name of the service to decorate.
11304 * @param {Function} This function will be invoked when the service needs to be
11305 * instantiated and should return the decorated service instance.
11307 * See {@link auto.$provide#decorator $provide.decorator()}.
11309 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
11313 * @name angular.Module#animation
11315 * @param {string} name animation name
11316 * @param {Function} animationFactory Factory function for creating new instance of an
11320 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
11323 * Defines an animation hook that can be later used with
11324 * {@link $animate $animate} service and directives that use this service.
11327 * module.animation('.animation-name', function($inject1, $inject2) {
11329 * eventName : function(element, done) {
11330 * //code to run the animation
11331 * //once complete, then run done()
11332 * return function cancellationFunction(element) {
11333 * //code to cancel the animation
11340 * See {@link ng.$animateProvider#register $animateProvider.register()} and
11341 * {@link ngAnimate ngAnimate module} for more information.
11343 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
11347 * @name angular.Module#filter
11349 * @param {string} name Filter name - this must be a valid angular expression identifier
11350 * @param {Function} filterFactory Factory function for creating new instance of filter.
11352 * See {@link ng.$filterProvider#register $filterProvider.register()}.
11354 * <div class="alert alert-warning">
11355 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
11356 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
11357 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
11358 * (`myapp_subsection_filterx`).
11361 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
11365 * @name angular.Module#controller
11367 * @param {string|Object} name Controller name, or an object map of controllers where the
11368 * keys are the names and the values are the constructors.
11369 * @param {Function} constructor Controller constructor function.
11371 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
11373 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
11377 * @name angular.Module#directive
11379 * @param {string|Object} name Directive name, or an object map of directives where the
11380 * keys are the names and the values are the factories.
11381 * @param {Function} directiveFactory Factory function for creating new instance of
11384 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
11386 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
11390 * @name angular.Module#config
11392 * @param {Function} configFn Execute this function on module load. Useful for service
11395 * Use this method to register work which needs to be performed on module loading.
11396 * For more about how to configure services, see
11397 * {@link providers#provider-recipe Provider Recipe}.
11403 * @name angular.Module#run
11405 * @param {Function} initializationFn Execute this function after injector creation.
11406 * Useful for application initialization.
11408 * Use this method to register work which should be performed when the injector is done
11409 * loading all modules.
11411 run: function(block) {
11412 runBlocks.push(block);
11421 return moduleInstance;
11424 * @param {string} provider
11425 * @param {string} method
11426 * @param {String=} insertMethod
11427 * @returns {angular.Module}
11429 function invokeLater(provider, method, insertMethod, queue) {
11430 if (!queue) queue = invokeQueue;
11431 return function() {
11432 queue[insertMethod || 'push']([provider, method, arguments]);
11433 return moduleInstance;
11438 * @param {string} provider
11439 * @param {string} method
11440 * @returns {angular.Module}
11442 function invokeLaterAndSetModuleName(provider, method) {
11443 return function(recipeName, factoryFunction) {
11444 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
11445 invokeQueue.push([provider, method, arguments]);
11446 return moduleInstance;
11455 /* global: toDebugString: true */
11457 function serializeObject(obj) {
11460 return JSON.stringify(obj, function(key, val) {
11461 val = toJsonReplacer(key, val);
11462 if (isObject(val)) {
11464 if (seen.indexOf(val) >= 0) return '...';
11472 function toDebugString(obj) {
11473 if (typeof obj === 'function') {
11474 return obj.toString().replace(/ \{[\s\S]*$/, '');
11475 } else if (isUndefined(obj)) {
11476 return 'undefined';
11477 } else if (typeof obj !== 'string') {
11478 return serializeObject(obj);
11483 /* global angularModule: true,
11488 htmlAnchorDirective,
11497 ngBindHtmlDirective,
11498 ngBindTemplateDirective,
11500 ngClassEvenDirective,
11501 ngClassOddDirective,
11503 ngControllerDirective,
11507 ngIncludeDirective,
11508 ngIncludeFillContentDirective,
11510 ngNonBindableDirective,
11511 ngPluralizeDirective,
11516 ngSwitchWhenDirective,
11517 ngSwitchDefaultDirective,
11518 ngOptionsDirective,
11519 ngTranscludeDirective,
11527 minlengthDirective,
11528 minlengthDirective,
11529 maxlengthDirective,
11530 maxlengthDirective,
11532 ngModelOptionsDirective,
11533 ngAttributeAliasDirectives,
11536 $AnchorScrollProvider,
11538 $CoreAnimateCssProvider,
11539 $$CoreAnimateQueueProvider,
11540 $$CoreAnimateRunnerProvider,
11542 $CacheFactoryProvider,
11543 $ControllerProvider,
11545 $ExceptionHandlerProvider,
11547 $$ForceReflowProvider,
11548 $InterpolateProvider,
11552 $HttpParamSerializerProvider,
11553 $HttpParamSerializerJQLikeProvider,
11554 $HttpBackendProvider,
11555 $xhrFactoryProvider,
11559 $RootScopeProvider,
11562 $$SanitizeUriProvider,
11564 $SceDelegateProvider,
11566 $TemplateCacheProvider,
11567 $TemplateRequestProvider,
11568 $$TestabilityProvider,
11573 $$CookieReaderProvider
11579 * @name angular.version
11582 * An object that contains information about the current AngularJS version.
11584 * This object has the following properties:
11586 * - `full` – `{string}` – Full version string, such as "0.9.18".
11587 * - `major` – `{number}` – Major version number, such as "0".
11588 * - `minor` – `{number}` – Minor version number, such as "9".
11589 * - `dot` – `{number}` – Dot version number, such as "18".
11590 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
11593 full: '1.4.8', // all of these placeholder strings will be replaced by grunt's
11594 major: 1, // package task
11597 codeName: 'ice-manipulation'
11601 function publishExternalAPI(angular) {
11603 'bootstrap': bootstrap,
11609 'forEach': forEach,
11610 'injector': createInjector,
11614 'fromJson': fromJson,
11615 'identity': identity,
11616 'isUndefined': isUndefined,
11617 'isDefined': isDefined,
11618 'isString': isString,
11619 'isFunction': isFunction,
11620 'isObject': isObject,
11621 'isNumber': isNumber,
11622 'isElement': isElement,
11623 'isArray': isArray,
11624 'version': version,
11626 'lowercase': lowercase,
11627 'uppercase': uppercase,
11628 'callbacks': {counter: 0},
11629 'getTestability': getTestability,
11630 '$$minErr': minErr,
11632 'reloadWithDebugInfo': reloadWithDebugInfo
11635 angularModule = setupModuleLoader(window);
11637 angularModule('ng', ['ngLocale'], ['$provide',
11638 function ngModule($provide) {
11639 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
11640 $provide.provider({
11641 $$sanitizeUri: $$SanitizeUriProvider
11643 $provide.provider('$compile', $CompileProvider).
11645 a: htmlAnchorDirective,
11646 input: inputDirective,
11647 textarea: inputDirective,
11648 form: formDirective,
11649 script: scriptDirective,
11650 select: selectDirective,
11651 style: styleDirective,
11652 option: optionDirective,
11653 ngBind: ngBindDirective,
11654 ngBindHtml: ngBindHtmlDirective,
11655 ngBindTemplate: ngBindTemplateDirective,
11656 ngClass: ngClassDirective,
11657 ngClassEven: ngClassEvenDirective,
11658 ngClassOdd: ngClassOddDirective,
11659 ngCloak: ngCloakDirective,
11660 ngController: ngControllerDirective,
11661 ngForm: ngFormDirective,
11662 ngHide: ngHideDirective,
11663 ngIf: ngIfDirective,
11664 ngInclude: ngIncludeDirective,
11665 ngInit: ngInitDirective,
11666 ngNonBindable: ngNonBindableDirective,
11667 ngPluralize: ngPluralizeDirective,
11668 ngRepeat: ngRepeatDirective,
11669 ngShow: ngShowDirective,
11670 ngStyle: ngStyleDirective,
11671 ngSwitch: ngSwitchDirective,
11672 ngSwitchWhen: ngSwitchWhenDirective,
11673 ngSwitchDefault: ngSwitchDefaultDirective,
11674 ngOptions: ngOptionsDirective,
11675 ngTransclude: ngTranscludeDirective,
11676 ngModel: ngModelDirective,
11677 ngList: ngListDirective,
11678 ngChange: ngChangeDirective,
11679 pattern: patternDirective,
11680 ngPattern: patternDirective,
11681 required: requiredDirective,
11682 ngRequired: requiredDirective,
11683 minlength: minlengthDirective,
11684 ngMinlength: minlengthDirective,
11685 maxlength: maxlengthDirective,
11686 ngMaxlength: maxlengthDirective,
11687 ngValue: ngValueDirective,
11688 ngModelOptions: ngModelOptionsDirective
11691 ngInclude: ngIncludeFillContentDirective
11693 directive(ngAttributeAliasDirectives).
11694 directive(ngEventDirectives);
11695 $provide.provider({
11696 $anchorScroll: $AnchorScrollProvider,
11697 $animate: $AnimateProvider,
11698 $animateCss: $CoreAnimateCssProvider,
11699 $$animateQueue: $$CoreAnimateQueueProvider,
11700 $$AnimateRunner: $$CoreAnimateRunnerProvider,
11701 $browser: $BrowserProvider,
11702 $cacheFactory: $CacheFactoryProvider,
11703 $controller: $ControllerProvider,
11704 $document: $DocumentProvider,
11705 $exceptionHandler: $ExceptionHandlerProvider,
11706 $filter: $FilterProvider,
11707 $$forceReflow: $$ForceReflowProvider,
11708 $interpolate: $InterpolateProvider,
11709 $interval: $IntervalProvider,
11710 $http: $HttpProvider,
11711 $httpParamSerializer: $HttpParamSerializerProvider,
11712 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
11713 $httpBackend: $HttpBackendProvider,
11714 $xhrFactory: $xhrFactoryProvider,
11715 $location: $LocationProvider,
11716 $log: $LogProvider,
11717 $parse: $ParseProvider,
11718 $rootScope: $RootScopeProvider,
11721 $sce: $SceProvider,
11722 $sceDelegate: $SceDelegateProvider,
11723 $sniffer: $SnifferProvider,
11724 $templateCache: $TemplateCacheProvider,
11725 $templateRequest: $TemplateRequestProvider,
11726 $$testability: $$TestabilityProvider,
11727 $timeout: $TimeoutProvider,
11728 $window: $WindowProvider,
11729 $$rAF: $$RAFProvider,
11730 $$jqLite: $$jqLiteProvider,
11731 $$HashMap: $$HashMapProvider,
11732 $$cookieReader: $$CookieReaderProvider
11738 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
11739 * Any commits to this file should be reviewed with security in mind. *
11740 * Changes to this file can potentially create security vulnerabilities. *
11741 * An approval from 2 Core members with history of modifying *
11742 * this file is required. *
11744 * Does the change somehow allow for arbitrary javascript to be executed? *
11745 * Or allows for someone to change the prototype of built-in objects? *
11746 * Or gives undesired access to variables likes document or window? *
11747 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
11749 /* global JQLitePrototype: true,
11750 addEventListenerFn: true,
11751 removeEventListenerFn: true,
11752 BOOLEAN_ATTR: true,
11753 ALIASED_ATTR: true,
11756 //////////////////////////////////
11758 //////////////////////////////////
11762 * @name angular.element
11767 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
11769 * If jQuery is available, `angular.element` is an alias for the
11770 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
11771 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
11773 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
11774 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
11775 * commonly needed functionality with the goal of having a very small footprint.</div>
11777 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
11779 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
11780 * jqLite; they are never raw DOM references.</div>
11782 * ## Angular's jqLite
11783 * jqLite provides only the following jQuery methods:
11785 * - [`addClass()`](http://api.jquery.com/addClass/)
11786 * - [`after()`](http://api.jquery.com/after/)
11787 * - [`append()`](http://api.jquery.com/append/)
11788 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
11789 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
11790 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
11791 * - [`clone()`](http://api.jquery.com/clone/)
11792 * - [`contents()`](http://api.jquery.com/contents/)
11793 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
11794 * - [`data()`](http://api.jquery.com/data/)
11795 * - [`detach()`](http://api.jquery.com/detach/)
11796 * - [`empty()`](http://api.jquery.com/empty/)
11797 * - [`eq()`](http://api.jquery.com/eq/)
11798 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
11799 * - [`hasClass()`](http://api.jquery.com/hasClass/)
11800 * - [`html()`](http://api.jquery.com/html/)
11801 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
11802 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
11803 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
11804 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
11805 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
11806 * - [`prepend()`](http://api.jquery.com/prepend/)
11807 * - [`prop()`](http://api.jquery.com/prop/)
11808 * - [`ready()`](http://api.jquery.com/ready/)
11809 * - [`remove()`](http://api.jquery.com/remove/)
11810 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
11811 * - [`removeClass()`](http://api.jquery.com/removeClass/)
11812 * - [`removeData()`](http://api.jquery.com/removeData/)
11813 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
11814 * - [`text()`](http://api.jquery.com/text/)
11815 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
11816 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
11817 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
11818 * - [`val()`](http://api.jquery.com/val/)
11819 * - [`wrap()`](http://api.jquery.com/wrap/)
11821 * ## jQuery/jqLite Extras
11822 * Angular also provides the following additional methods and events to both jQuery and jqLite:
11825 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
11826 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
11827 * element before it is removed.
11830 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
11831 * retrieves controller associated with the `ngController` directive. If `name` is provided as
11832 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
11834 * - `injector()` - retrieves the injector of the current element or its parent.
11835 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
11836 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
11838 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
11839 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
11840 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
11841 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
11842 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
11843 * parent element is reached.
11845 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
11846 * @returns {Object} jQuery object.
11849 JQLite.expando = 'ng339';
11851 var jqCache = JQLite.cache = {},
11853 addEventListenerFn = function(element, type, fn) {
11854 element.addEventListener(type, fn, false);
11856 removeEventListenerFn = function(element, type, fn) {
11857 element.removeEventListener(type, fn, false);
11861 * !!! This is an undocumented "private" function !!!
11863 JQLite._data = function(node) {
11864 //jQuery always returns an object on cache miss
11865 return this.cache[node[this.expando]] || {};
11868 function jqNextId() { return ++jqId; }
11871 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
11872 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
11873 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
11874 var jqLiteMinErr = minErr('jqLite');
11877 * Converts snake_case to camelCase.
11878 * Also there is special case for Moz prefix starting with upper case letter.
11879 * @param name Name to normalize
11881 function camelCase(name) {
11883 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
11884 return offset ? letter.toUpperCase() : letter;
11886 replace(MOZ_HACK_REGEXP, 'Moz$1');
11889 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
11890 var HTML_REGEXP = /<|&#?\w+;/;
11891 var TAG_NAME_REGEXP = /<([\w:-]+)/;
11892 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
11895 'option': [1, '<select multiple="multiple">', '</select>'],
11897 'thead': [1, '<table>', '</table>'],
11898 'col': [2, '<table><colgroup>', '</colgroup></table>'],
11899 'tr': [2, '<table><tbody>', '</tbody></table>'],
11900 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
11901 '_default': [0, "", ""]
11904 wrapMap.optgroup = wrapMap.option;
11905 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
11906 wrapMap.th = wrapMap.td;
11909 function jqLiteIsTextNode(html) {
11910 return !HTML_REGEXP.test(html);
11913 function jqLiteAcceptsData(node) {
11914 // The window object can accept data but has no nodeType
11915 // Otherwise we are only interested in elements (1) and documents (9)
11916 var nodeType = node.nodeType;
11917 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
11920 function jqLiteHasData(node) {
11921 for (var key in jqCache[node.ng339]) {
11927 function jqLiteBuildFragment(html, context) {
11928 var tmp, tag, wrap,
11929 fragment = context.createDocumentFragment(),
11932 if (jqLiteIsTextNode(html)) {
11933 // Convert non-html into a text node
11934 nodes.push(context.createTextNode(html));
11936 // Convert html into DOM nodes
11937 tmp = tmp || fragment.appendChild(context.createElement("div"));
11938 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
11939 wrap = wrapMap[tag] || wrapMap._default;
11940 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
11942 // Descend through wrappers to the right content
11945 tmp = tmp.lastChild;
11948 nodes = concat(nodes, tmp.childNodes);
11950 tmp = fragment.firstChild;
11951 tmp.textContent = "";
11954 // Remove wrapper from fragment
11955 fragment.textContent = "";
11956 fragment.innerHTML = ""; // Clear inner HTML
11957 forEach(nodes, function(node) {
11958 fragment.appendChild(node);
11964 function jqLiteParseHTML(html, context) {
11965 context = context || document;
11968 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
11969 return [context.createElement(parsed[1])];
11972 if ((parsed = jqLiteBuildFragment(html, context))) {
11973 return parsed.childNodes;
11980 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
11981 var jqLiteContains = Node.prototype.contains || function(arg) {
11982 // jshint bitwise: false
11983 return !!(this.compareDocumentPosition(arg) & 16);
11984 // jshint bitwise: true
11987 /////////////////////////////////////////////
11988 function JQLite(element) {
11989 if (element instanceof JQLite) {
11995 if (isString(element)) {
11996 element = trim(element);
11997 argIsString = true;
11999 if (!(this instanceof JQLite)) {
12000 if (argIsString && element.charAt(0) != '<') {
12001 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
12003 return new JQLite(element);
12007 jqLiteAddNodes(this, jqLiteParseHTML(element));
12009 jqLiteAddNodes(this, element);
12013 function jqLiteClone(element) {
12014 return element.cloneNode(true);
12017 function jqLiteDealoc(element, onlyDescendants) {
12018 if (!onlyDescendants) jqLiteRemoveData(element);
12020 if (element.querySelectorAll) {
12021 var descendants = element.querySelectorAll('*');
12022 for (var i = 0, l = descendants.length; i < l; i++) {
12023 jqLiteRemoveData(descendants[i]);
12028 function jqLiteOff(element, type, fn, unsupported) {
12029 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
12031 var expandoStore = jqLiteExpandoStore(element);
12032 var events = expandoStore && expandoStore.events;
12033 var handle = expandoStore && expandoStore.handle;
12035 if (!handle) return; //no listeners registered
12038 for (type in events) {
12039 if (type !== '$destroy') {
12040 removeEventListenerFn(element, type, handle);
12042 delete events[type];
12046 var removeHandler = function(type) {
12047 var listenerFns = events[type];
12048 if (isDefined(fn)) {
12049 arrayRemove(listenerFns || [], fn);
12051 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
12052 removeEventListenerFn(element, type, handle);
12053 delete events[type];
12057 forEach(type.split(' '), function(type) {
12058 removeHandler(type);
12059 if (MOUSE_EVENT_MAP[type]) {
12060 removeHandler(MOUSE_EVENT_MAP[type]);
12066 function jqLiteRemoveData(element, name) {
12067 var expandoId = element.ng339;
12068 var expandoStore = expandoId && jqCache[expandoId];
12070 if (expandoStore) {
12072 delete expandoStore.data[name];
12076 if (expandoStore.handle) {
12077 if (expandoStore.events.$destroy) {
12078 expandoStore.handle({}, '$destroy');
12080 jqLiteOff(element);
12082 delete jqCache[expandoId];
12083 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
12088 function jqLiteExpandoStore(element, createIfNecessary) {
12089 var expandoId = element.ng339,
12090 expandoStore = expandoId && jqCache[expandoId];
12092 if (createIfNecessary && !expandoStore) {
12093 element.ng339 = expandoId = jqNextId();
12094 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
12097 return expandoStore;
12101 function jqLiteData(element, key, value) {
12102 if (jqLiteAcceptsData(element)) {
12104 var isSimpleSetter = isDefined(value);
12105 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
12106 var massGetter = !key;
12107 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
12108 var data = expandoStore && expandoStore.data;
12110 if (isSimpleSetter) { // data('key', value)
12113 if (massGetter) { // data()
12116 if (isSimpleGetter) { // data('key')
12117 // don't force creation of expandoStore if it doesn't exist yet
12118 return data && data[key];
12119 } else { // mass-setter: data({key1: val1, key2: val2})
12127 function jqLiteHasClass(element, selector) {
12128 if (!element.getAttribute) return false;
12129 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
12130 indexOf(" " + selector + " ") > -1);
12133 function jqLiteRemoveClass(element, cssClasses) {
12134 if (cssClasses && element.setAttribute) {
12135 forEach(cssClasses.split(' '), function(cssClass) {
12136 element.setAttribute('class', trim(
12137 (" " + (element.getAttribute('class') || '') + " ")
12138 .replace(/[\n\t]/g, " ")
12139 .replace(" " + trim(cssClass) + " ", " "))
12145 function jqLiteAddClass(element, cssClasses) {
12146 if (cssClasses && element.setAttribute) {
12147 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
12148 .replace(/[\n\t]/g, " ");
12150 forEach(cssClasses.split(' '), function(cssClass) {
12151 cssClass = trim(cssClass);
12152 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
12153 existingClasses += cssClass + ' ';
12157 element.setAttribute('class', trim(existingClasses));
12162 function jqLiteAddNodes(root, elements) {
12163 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
12167 // if a Node (the most common case)
12168 if (elements.nodeType) {
12169 root[root.length++] = elements;
12171 var length = elements.length;
12173 // if an Array or NodeList and not a Window
12174 if (typeof length === 'number' && elements.window !== elements) {
12176 for (var i = 0; i < length; i++) {
12177 root[root.length++] = elements[i];
12181 root[root.length++] = elements;
12188 function jqLiteController(element, name) {
12189 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
12192 function jqLiteInheritedData(element, name, value) {
12193 // if element is the document object work with the html element instead
12194 // this makes $(document).scope() possible
12195 if (element.nodeType == NODE_TYPE_DOCUMENT) {
12196 element = element.documentElement;
12198 var names = isArray(name) ? name : [name];
12201 for (var i = 0, ii = names.length; i < ii; i++) {
12202 if (isDefined(value = jqLite.data(element, names[i]))) return value;
12205 // If dealing with a document fragment node with a host element, and no parent, use the host
12206 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
12207 // to lookup parent controllers.
12208 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
12212 function jqLiteEmpty(element) {
12213 jqLiteDealoc(element, true);
12214 while (element.firstChild) {
12215 element.removeChild(element.firstChild);
12219 function jqLiteRemove(element, keepData) {
12220 if (!keepData) jqLiteDealoc(element);
12221 var parent = element.parentNode;
12222 if (parent) parent.removeChild(element);
12226 function jqLiteDocumentLoaded(action, win) {
12227 win = win || window;
12228 if (win.document.readyState === 'complete') {
12229 // Force the action to be run async for consistent behaviour
12230 // from the action's point of view
12231 // i.e. it will definitely not be in a $apply
12232 win.setTimeout(action);
12234 // No need to unbind this handler as load is only ever called once
12235 jqLite(win).on('load', action);
12239 //////////////////////////////////////////
12240 // Functions which are declared directly.
12241 //////////////////////////////////////////
12242 var JQLitePrototype = JQLite.prototype = {
12243 ready: function(fn) {
12246 function trigger() {
12252 // check if document is already loaded
12253 if (document.readyState === 'complete') {
12254 setTimeout(trigger);
12256 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
12257 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
12259 JQLite(window).on('load', trigger); // fallback to window.onload for others
12263 toString: function() {
12265 forEach(this, function(e) { value.push('' + e);});
12266 return '[' + value.join(', ') + ']';
12269 eq: function(index) {
12270 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
12279 //////////////////////////////////////////
12280 // Functions iterating getter/setters.
12281 // these functions return self on setter and
12283 //////////////////////////////////////////
12284 var BOOLEAN_ATTR = {};
12285 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
12286 BOOLEAN_ATTR[lowercase(value)] = value;
12288 var BOOLEAN_ELEMENTS = {};
12289 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
12290 BOOLEAN_ELEMENTS[value] = true;
12292 var ALIASED_ATTR = {
12293 'ngMinlength': 'minlength',
12294 'ngMaxlength': 'maxlength',
12297 'ngPattern': 'pattern'
12300 function getBooleanAttrName(element, name) {
12301 // check dom last since we will most likely fail on name
12302 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
12304 // booleanAttr is here twice to minimize DOM access
12305 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
12308 function getAliasedAttrName(name) {
12309 return ALIASED_ATTR[name];
12314 removeData: jqLiteRemoveData,
12315 hasData: jqLiteHasData
12316 }, function(fn, name) {
12322 inheritedData: jqLiteInheritedData,
12324 scope: function(element) {
12325 // Can't use jqLiteData here directly so we stay compatible with jQuery!
12326 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
12329 isolateScope: function(element) {
12330 // Can't use jqLiteData here directly so we stay compatible with jQuery!
12331 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
12334 controller: jqLiteController,
12336 injector: function(element) {
12337 return jqLiteInheritedData(element, '$injector');
12340 removeAttr: function(element, name) {
12341 element.removeAttribute(name);
12344 hasClass: jqLiteHasClass,
12346 css: function(element, name, value) {
12347 name = camelCase(name);
12349 if (isDefined(value)) {
12350 element.style[name] = value;
12352 return element.style[name];
12356 attr: function(element, name, value) {
12357 var nodeType = element.nodeType;
12358 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
12361 var lowercasedName = lowercase(name);
12362 if (BOOLEAN_ATTR[lowercasedName]) {
12363 if (isDefined(value)) {
12365 element[name] = true;
12366 element.setAttribute(name, lowercasedName);
12368 element[name] = false;
12369 element.removeAttribute(lowercasedName);
12372 return (element[name] ||
12373 (element.attributes.getNamedItem(name) || noop).specified)
12377 } else if (isDefined(value)) {
12378 element.setAttribute(name, value);
12379 } else if (element.getAttribute) {
12380 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
12381 // some elements (e.g. Document) don't have get attribute, so return undefined
12382 var ret = element.getAttribute(name, 2);
12383 // normalize non-existing attributes to undefined (as jQuery)
12384 return ret === null ? undefined : ret;
12388 prop: function(element, name, value) {
12389 if (isDefined(value)) {
12390 element[name] = value;
12392 return element[name];
12396 text: (function() {
12400 function getText(element, value) {
12401 if (isUndefined(value)) {
12402 var nodeType = element.nodeType;
12403 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
12405 element.textContent = value;
12409 val: function(element, value) {
12410 if (isUndefined(value)) {
12411 if (element.multiple && nodeName_(element) === 'select') {
12413 forEach(element.options, function(option) {
12414 if (option.selected) {
12415 result.push(option.value || option.text);
12418 return result.length === 0 ? null : result;
12420 return element.value;
12422 element.value = value;
12425 html: function(element, value) {
12426 if (isUndefined(value)) {
12427 return element.innerHTML;
12429 jqLiteDealoc(element, true);
12430 element.innerHTML = value;
12434 }, function(fn, name) {
12436 * Properties: writes return selection, reads return first value
12438 JQLite.prototype[name] = function(arg1, arg2) {
12440 var nodeCount = this.length;
12442 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
12443 // in a way that survives minification.
12444 // jqLiteEmpty takes no arguments but is a setter.
12445 if (fn !== jqLiteEmpty &&
12446 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
12447 if (isObject(arg1)) {
12449 // we are a write, but the object properties are the key/values
12450 for (i = 0; i < nodeCount; i++) {
12451 if (fn === jqLiteData) {
12452 // data() takes the whole object in jQuery
12455 for (key in arg1) {
12456 fn(this[i], key, arg1[key]);
12460 // return self for chaining
12463 // we are a read, so read the first child.
12464 // TODO: do we still need this?
12465 var value = fn.$dv;
12466 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
12467 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
12468 for (var j = 0; j < jj; j++) {
12469 var nodeValue = fn(this[j], arg1, arg2);
12470 value = value ? value + nodeValue : nodeValue;
12475 // we are a write, so apply to all children
12476 for (i = 0; i < nodeCount; i++) {
12477 fn(this[i], arg1, arg2);
12479 // return self for chaining
12485 function createEventHandler(element, events) {
12486 var eventHandler = function(event, type) {
12487 // jQuery specific api
12488 event.isDefaultPrevented = function() {
12489 return event.defaultPrevented;
12492 var eventFns = events[type || event.type];
12493 var eventFnsLength = eventFns ? eventFns.length : 0;
12495 if (!eventFnsLength) return;
12497 if (isUndefined(event.immediatePropagationStopped)) {
12498 var originalStopImmediatePropagation = event.stopImmediatePropagation;
12499 event.stopImmediatePropagation = function() {
12500 event.immediatePropagationStopped = true;
12502 if (event.stopPropagation) {
12503 event.stopPropagation();
12506 if (originalStopImmediatePropagation) {
12507 originalStopImmediatePropagation.call(event);
12512 event.isImmediatePropagationStopped = function() {
12513 return event.immediatePropagationStopped === true;
12516 // Some events have special handlers that wrap the real handler
12517 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
12519 // Copy event handlers in case event handlers array is modified during execution.
12520 if ((eventFnsLength > 1)) {
12521 eventFns = shallowCopy(eventFns);
12524 for (var i = 0; i < eventFnsLength; i++) {
12525 if (!event.isImmediatePropagationStopped()) {
12526 handlerWrapper(element, event, eventFns[i]);
12531 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
12532 // events on `element`
12533 eventHandler.elem = element;
12534 return eventHandler;
12537 function defaultHandlerWrapper(element, event, handler) {
12538 handler.call(element, event);
12541 function specialMouseHandlerWrapper(target, event, handler) {
12542 // Refer to jQuery's implementation of mouseenter & mouseleave
12543 // Read about mouseenter and mouseleave:
12544 // http://www.quirksmode.org/js/events_mouse.html#link8
12545 var related = event.relatedTarget;
12546 // For mousenter/leave call the handler if related is outside the target.
12547 // NB: No relatedTarget if the mouse left/entered the browser window
12548 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
12549 handler.call(target, event);
12553 //////////////////////////////////////////
12554 // Functions iterating traversal.
12555 // These functions chain results into a single
12557 //////////////////////////////////////////
12559 removeData: jqLiteRemoveData,
12561 on: function jqLiteOn(element, type, fn, unsupported) {
12562 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
12564 // Do not add event handlers to non-elements because they will not be cleaned up.
12565 if (!jqLiteAcceptsData(element)) {
12569 var expandoStore = jqLiteExpandoStore(element, true);
12570 var events = expandoStore.events;
12571 var handle = expandoStore.handle;
12574 handle = expandoStore.handle = createEventHandler(element, events);
12577 // http://jsperf.com/string-indexof-vs-split
12578 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
12579 var i = types.length;
12581 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
12582 var eventFns = events[type];
12585 eventFns = events[type] = [];
12586 eventFns.specialHandlerWrapper = specialHandlerWrapper;
12587 if (type !== '$destroy' && !noEventListener) {
12588 addEventListenerFn(element, type, handle);
12597 if (MOUSE_EVENT_MAP[type]) {
12598 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
12599 addHandler(type, undefined, true);
12608 one: function(element, type, fn) {
12609 element = jqLite(element);
12611 //add the listener twice so that when it is called
12612 //you can remove the original function and still be
12613 //able to call element.off(ev, fn) normally
12614 element.on(type, function onFn() {
12615 element.off(type, fn);
12616 element.off(type, onFn);
12618 element.on(type, fn);
12621 replaceWith: function(element, replaceNode) {
12622 var index, parent = element.parentNode;
12623 jqLiteDealoc(element);
12624 forEach(new JQLite(replaceNode), function(node) {
12626 parent.insertBefore(node, index.nextSibling);
12628 parent.replaceChild(node, element);
12634 children: function(element) {
12636 forEach(element.childNodes, function(element) {
12637 if (element.nodeType === NODE_TYPE_ELEMENT) {
12638 children.push(element);
12644 contents: function(element) {
12645 return element.contentDocument || element.childNodes || [];
12648 append: function(element, node) {
12649 var nodeType = element.nodeType;
12650 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
12652 node = new JQLite(node);
12654 for (var i = 0, ii = node.length; i < ii; i++) {
12655 var child = node[i];
12656 element.appendChild(child);
12660 prepend: function(element, node) {
12661 if (element.nodeType === NODE_TYPE_ELEMENT) {
12662 var index = element.firstChild;
12663 forEach(new JQLite(node), function(child) {
12664 element.insertBefore(child, index);
12669 wrap: function(element, wrapNode) {
12670 wrapNode = jqLite(wrapNode).eq(0).clone()[0];
12671 var parent = element.parentNode;
12673 parent.replaceChild(wrapNode, element);
12675 wrapNode.appendChild(element);
12678 remove: jqLiteRemove,
12680 detach: function(element) {
12681 jqLiteRemove(element, true);
12684 after: function(element, newElement) {
12685 var index = element, parent = element.parentNode;
12686 newElement = new JQLite(newElement);
12688 for (var i = 0, ii = newElement.length; i < ii; i++) {
12689 var node = newElement[i];
12690 parent.insertBefore(node, index.nextSibling);
12695 addClass: jqLiteAddClass,
12696 removeClass: jqLiteRemoveClass,
12698 toggleClass: function(element, selector, condition) {
12700 forEach(selector.split(' '), function(className) {
12701 var classCondition = condition;
12702 if (isUndefined(classCondition)) {
12703 classCondition = !jqLiteHasClass(element, className);
12705 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
12710 parent: function(element) {
12711 var parent = element.parentNode;
12712 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
12715 next: function(element) {
12716 return element.nextElementSibling;
12719 find: function(element, selector) {
12720 if (element.getElementsByTagName) {
12721 return element.getElementsByTagName(selector);
12727 clone: jqLiteClone,
12729 triggerHandler: function(element, event, extraParameters) {
12731 var dummyEvent, eventFnsCopy, handlerArgs;
12732 var eventName = event.type || event;
12733 var expandoStore = jqLiteExpandoStore(element);
12734 var events = expandoStore && expandoStore.events;
12735 var eventFns = events && events[eventName];
12738 // Create a dummy event to pass to the handlers
12740 preventDefault: function() { this.defaultPrevented = true; },
12741 isDefaultPrevented: function() { return this.defaultPrevented === true; },
12742 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
12743 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
12744 stopPropagation: noop,
12749 // If a custom event was provided then extend our dummy event with it
12751 dummyEvent = extend(dummyEvent, event);
12754 // Copy event handlers in case event handlers array is modified during execution.
12755 eventFnsCopy = shallowCopy(eventFns);
12756 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
12758 forEach(eventFnsCopy, function(fn) {
12759 if (!dummyEvent.isImmediatePropagationStopped()) {
12760 fn.apply(element, handlerArgs);
12765 }, function(fn, name) {
12767 * chaining functions
12769 JQLite.prototype[name] = function(arg1, arg2, arg3) {
12772 for (var i = 0, ii = this.length; i < ii; i++) {
12773 if (isUndefined(value)) {
12774 value = fn(this[i], arg1, arg2, arg3);
12775 if (isDefined(value)) {
12776 // any function which returns a value needs to be wrapped
12777 value = jqLite(value);
12780 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
12783 return isDefined(value) ? value : this;
12786 // bind legacy bind/unbind to on/off
12787 JQLite.prototype.bind = JQLite.prototype.on;
12788 JQLite.prototype.unbind = JQLite.prototype.off;
12792 // Provider for private $$jqLite service
12793 function $$jqLiteProvider() {
12794 this.$get = function $$jqLite() {
12795 return extend(JQLite, {
12796 hasClass: function(node, classes) {
12797 if (node.attr) node = node[0];
12798 return jqLiteHasClass(node, classes);
12800 addClass: function(node, classes) {
12801 if (node.attr) node = node[0];
12802 return jqLiteAddClass(node, classes);
12804 removeClass: function(node, classes) {
12805 if (node.attr) node = node[0];
12806 return jqLiteRemoveClass(node, classes);
12813 * Computes a hash of an 'obj'.
12816 * number is number as string
12817 * object is either result of calling $$hashKey function on the object or uniquely generated id,
12818 * that is also assigned to the $$hashKey property of the object.
12821 * @returns {string} hash string such that the same input will have the same hash string.
12822 * The resulting string key is in 'type:hashKey' format.
12824 function hashKey(obj, nextUidFn) {
12825 var key = obj && obj.$$hashKey;
12828 if (typeof key === 'function') {
12829 key = obj.$$hashKey();
12834 var objType = typeof obj;
12835 if (objType == 'function' || (objType == 'object' && obj !== null)) {
12836 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
12838 key = objType + ':' + obj;
12845 * HashMap which can use objects as keys
12847 function HashMap(array, isolatedUid) {
12850 this.nextUid = function() {
12854 forEach(array, this.put, this);
12856 HashMap.prototype = {
12858 * Store key value pair
12859 * @param key key to store can be any type
12860 * @param value value to store can be any type
12862 put: function(key, value) {
12863 this[hashKey(key, this.nextUid)] = value;
12868 * @returns {Object} the value for the key
12870 get: function(key) {
12871 return this[hashKey(key, this.nextUid)];
12875 * Remove the key/value pair
12878 remove: function(key) {
12879 var value = this[key = hashKey(key, this.nextUid)];
12885 var $$HashMapProvider = [function() {
12886 this.$get = [function() {
12894 * @name angular.injector
12898 * Creates an injector object that can be used for retrieving services as well as for
12899 * dependency injection (see {@link guide/di dependency injection}).
12901 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
12902 * {@link angular.module}. The `ng` module must be explicitly added.
12903 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
12904 * disallows argument name annotation inference.
12905 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
12910 * // create an injector
12911 * var $injector = angular.injector(['ng']);
12913 * // use the injector to kick off your application
12914 * // use the type inference to auto inject arguments, or use implicit injection
12915 * $injector.invoke(function($rootScope, $compile, $document) {
12916 * $compile($document)($rootScope);
12917 * $rootScope.$digest();
12921 * Sometimes you want to get access to the injector of a currently running Angular app
12922 * from outside Angular. Perhaps, you want to inject and compile some markup after the
12923 * application has been bootstrapped. You can do this using the extra `injector()` added
12924 * to JQuery/jqLite elements. See {@link angular.element}.
12926 * *This is fairly rare but could be the case if a third party library is injecting the
12929 * In the following example a new block of HTML containing a `ng-controller`
12930 * directive is added to the end of the document body by JQuery. We then compile and link
12931 * it into the current AngularJS scope.
12934 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
12935 * $(document.body).append($div);
12937 * angular.element(document).injector().invoke(function($compile) {
12938 * var scope = angular.element($div).scope();
12939 * $compile($div)(scope);
12950 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
12953 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
12954 var FN_ARG_SPLIT = /,/;
12955 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
12956 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
12957 var $injectorMinErr = minErr('$injector');
12959 function anonFn(fn) {
12960 // For anonymous functions, showing at the very least the function signature can help in
12962 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
12963 args = fnText.match(FN_ARGS);
12965 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
12970 function annotate(fn, strictDi, name) {
12976 if (typeof fn === 'function') {
12977 if (!($inject = fn.$inject)) {
12981 if (!isString(name) || !name) {
12982 name = fn.name || anonFn(fn);
12984 throw $injectorMinErr('strictdi',
12985 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
12987 fnText = fn.toString().replace(STRIP_COMMENTS, '');
12988 argDecl = fnText.match(FN_ARGS);
12989 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
12990 arg.replace(FN_ARG, function(all, underscore, name) {
12991 $inject.push(name);
12995 fn.$inject = $inject;
12997 } else if (isArray(fn)) {
12998 last = fn.length - 1;
12999 assertArgFn(fn[last], 'fn');
13000 $inject = fn.slice(0, last);
13002 assertArgFn(fn, 'fn', true);
13007 ///////////////////////////////////////
13015 * `$injector` is used to retrieve object instances as defined by
13016 * {@link auto.$provide provider}, instantiate types, invoke methods,
13017 * and load modules.
13019 * The following always holds true:
13022 * var $injector = angular.injector();
13023 * expect($injector.get('$injector')).toBe($injector);
13024 * expect($injector.invoke(function($injector) {
13025 * return $injector;
13026 * })).toBe($injector);
13029 * # Injection Function Annotation
13031 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
13032 * following are all valid ways of annotating function with injection arguments and are equivalent.
13035 * // inferred (only works if code not minified/obfuscated)
13036 * $injector.invoke(function(serviceA){});
13039 * function explicit(serviceA) {};
13040 * explicit.$inject = ['serviceA'];
13041 * $injector.invoke(explicit);
13044 * $injector.invoke(['serviceA', function(serviceA){}]);
13049 * In JavaScript calling `toString()` on a function returns the function definition. The definition
13050 * can then be parsed and the function arguments can be extracted. This method of discovering
13051 * annotations is disallowed when the injector is in strict mode.
13052 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
13055 * ## `$inject` Annotation
13056 * By adding an `$inject` property onto a function the injection parameters can be specified.
13059 * As an array of injection names, where the last item in the array is the function to call.
13064 * @name $injector#get
13067 * Return an instance of the service.
13069 * @param {string} name The name of the instance to retrieve.
13070 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
13071 * @return {*} The instance.
13076 * @name $injector#invoke
13079 * Invoke the method and supply the method arguments from the `$injector`.
13081 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
13082 * injected according to the {@link guide/di $inject Annotation} rules.
13083 * @param {Object=} self The `this` for the invoked method.
13084 * @param {Object=} locals Optional object. If preset then any argument names are read from this
13085 * object first, before the `$injector` is consulted.
13086 * @returns {*} the value returned by the invoked `fn` function.
13091 * @name $injector#has
13094 * Allows the user to query if the particular service exists.
13096 * @param {string} name Name of the service to query.
13097 * @returns {boolean} `true` if injector has given service.
13102 * @name $injector#instantiate
13104 * Create a new instance of JS type. The method takes a constructor function, invokes the new
13105 * operator, and supplies all of the arguments to the constructor function as specified by the
13106 * constructor annotation.
13108 * @param {Function} Type Annotated constructor function.
13109 * @param {Object=} locals Optional object. If preset then any argument names are read from this
13110 * object first, before the `$injector` is consulted.
13111 * @returns {Object} new instance of `Type`.
13116 * @name $injector#annotate
13119 * Returns an array of service names which the function is requesting for injection. This API is
13120 * used by the injector to determine which services need to be injected into the function when the
13121 * function is invoked. There are three ways in which the function can be annotated with the needed
13126 * The simplest form is to extract the dependencies from the arguments of the function. This is done
13127 * by converting the function into a string using `toString()` method and extracting the argument
13131 * function MyController($scope, $route) {
13136 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
13139 * You can disallow this method by using strict injection mode.
13141 * This method does not work with code minification / obfuscation. For this reason the following
13142 * annotation strategies are supported.
13144 * # The `$inject` property
13146 * If a function has an `$inject` property and its value is an array of strings, then the strings
13147 * represent names of services to be injected into the function.
13150 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
13153 * // Define function dependencies
13154 * MyController['$inject'] = ['$scope', '$route'];
13157 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
13160 * # The array notation
13162 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
13163 * is very inconvenient. In these situations using the array notation to specify the dependencies in
13164 * a way that survives minification is a better choice:
13167 * // We wish to write this (not minification / obfuscation safe)
13168 * injector.invoke(function($compile, $rootScope) {
13172 * // We are forced to write break inlining
13173 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
13176 * tmpFn.$inject = ['$compile', '$rootScope'];
13177 * injector.invoke(tmpFn);
13179 * // To better support inline function the inline annotation is supported
13180 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
13185 * expect(injector.annotate(
13186 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
13187 * ).toEqual(['$compile', '$rootScope']);
13190 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
13191 * be retrieved as described above.
13193 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
13195 * @returns {Array.<string>} The names of the services which the function requires.
13207 * The {@link auto.$provide $provide} service has a number of methods for registering components
13208 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
13209 * {@link angular.Module}.
13211 * An Angular **service** is a singleton object created by a **service factory**. These **service
13212 * factories** are functions which, in turn, are created by a **service provider**.
13213 * The **service providers** are constructor functions. When instantiated they must contain a
13214 * property called `$get`, which holds the **service factory** function.
13216 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
13217 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
13218 * function to get the instance of the **service**.
13220 * Often services have no configuration options and there is no need to add methods to the service
13221 * provider. The provider will be no more than a constructor function with a `$get` property. For
13222 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
13223 * services without specifying a provider.
13225 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
13226 * {@link auto.$injector $injector}
13227 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
13228 * providers and services.
13229 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
13230 * services, not providers.
13231 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
13232 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
13233 * given factory function.
13234 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
13235 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
13236 * a new object using the given constructor function.
13238 * See the individual methods for more information and examples.
13243 * @name $provide#provider
13246 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
13247 * are constructor functions, whose instances are responsible for "providing" a factory for a
13250 * Service provider names start with the name of the service they provide followed by `Provider`.
13251 * For example, the {@link ng.$log $log} service has a provider called
13252 * {@link ng.$logProvider $logProvider}.
13254 * Service provider objects can have additional methods which allow configuration of the provider
13255 * and its service. Importantly, you can configure what kind of service is created by the `$get`
13256 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
13257 * method {@link ng.$logProvider#debugEnabled debugEnabled}
13258 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
13261 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
13263 * @param {(Object|function())} provider If the provider is:
13265 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
13266 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
13267 * - `Constructor`: a new instance of the provider will be created using
13268 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
13270 * @returns {Object} registered provider instance
13274 * The following example shows how to create a simple event tracking service and register it using
13275 * {@link auto.$provide#provider $provide.provider()}.
13278 * // Define the eventTracker provider
13279 * function EventTrackerProvider() {
13280 * var trackingUrl = '/track';
13282 * // A provider method for configuring where the tracked events should been saved
13283 * this.setTrackingUrl = function(url) {
13284 * trackingUrl = url;
13287 * // The service factory function
13288 * this.$get = ['$http', function($http) {
13289 * var trackedEvents = {};
13291 * // Call this to track an event
13292 * event: function(event) {
13293 * var count = trackedEvents[event] || 0;
13295 * trackedEvents[event] = count;
13298 * // Call this to save the tracked events to the trackingUrl
13299 * save: function() {
13300 * $http.post(trackingUrl, trackedEvents);
13306 * describe('eventTracker', function() {
13309 * beforeEach(module(function($provide) {
13310 * // Register the eventTracker provider
13311 * $provide.provider('eventTracker', EventTrackerProvider);
13314 * beforeEach(module(function(eventTrackerProvider) {
13315 * // Configure eventTracker provider
13316 * eventTrackerProvider.setTrackingUrl('/custom-track');
13319 * it('tracks events', inject(function(eventTracker) {
13320 * expect(eventTracker.event('login')).toEqual(1);
13321 * expect(eventTracker.event('login')).toEqual(2);
13324 * it('saves to the tracking url', inject(function(eventTracker, $http) {
13325 * postSpy = spyOn($http, 'post');
13326 * eventTracker.event('login');
13327 * eventTracker.save();
13328 * expect(postSpy).toHaveBeenCalled();
13329 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
13330 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
13331 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
13339 * @name $provide#factory
13342 * Register a **service factory**, which will be called to return the service instance.
13343 * This is short for registering a service where its provider consists of only a `$get` property,
13344 * which is the given service factory function.
13345 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
13346 * configure your service in a provider.
13348 * @param {string} name The name of the instance.
13349 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
13350 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
13351 * @returns {Object} registered provider instance
13354 * Here is an example of registering a service
13356 * $provide.factory('ping', ['$http', function($http) {
13357 * return function ping() {
13358 * return $http.send('/ping');
13362 * You would then inject and use this service like this:
13364 * someModule.controller('Ctrl', ['ping', function(ping) {
13373 * @name $provide#service
13376 * Register a **service constructor**, which will be invoked with `new` to create the service
13378 * This is short for registering a service where its provider's `$get` property is the service
13379 * constructor function that will be used to instantiate the service instance.
13381 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
13384 * @param {string} name The name of the instance.
13385 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
13386 * that will be instantiated.
13387 * @returns {Object} registered provider instance
13390 * Here is an example of registering a service using
13391 * {@link auto.$provide#service $provide.service(class)}.
13393 * var Ping = function($http) {
13394 * this.$http = $http;
13397 * Ping.$inject = ['$http'];
13399 * Ping.prototype.send = function() {
13400 * return this.$http.get('/ping');
13402 * $provide.service('ping', Ping);
13404 * You would then inject and use this service like this:
13406 * someModule.controller('Ctrl', ['ping', function(ping) {
13415 * @name $provide#value
13418 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
13419 * number, an array, an object or a function. This is short for registering a service where its
13420 * provider's `$get` property is a factory function that takes no arguments and returns the **value
13423 * Value services are similar to constant services, except that they cannot be injected into a
13424 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
13426 * {@link auto.$provide#decorator decorator}.
13428 * @param {string} name The name of the instance.
13429 * @param {*} value The value.
13430 * @returns {Object} registered provider instance
13433 * Here are some examples of creating value services.
13435 * $provide.value('ADMIN_USER', 'admin');
13437 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
13439 * $provide.value('halfOf', function(value) {
13440 * return value / 2;
13448 * @name $provide#constant
13451 * Register a **constant service**, such as a string, a number, an array, an object or a function,
13452 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
13453 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
13454 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
13456 * @param {string} name The name of the constant.
13457 * @param {*} value The constant value.
13458 * @returns {Object} registered instance
13461 * Here a some examples of creating constants:
13463 * $provide.constant('SHARD_HEIGHT', 306);
13465 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
13467 * $provide.constant('double', function(value) {
13468 * return value * 2;
13476 * @name $provide#decorator
13479 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
13480 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
13481 * service. The object returned by the decorator may be the original service, or a new service
13482 * object which replaces or wraps and delegates to the original service.
13484 * @param {string} name The name of the service to decorate.
13485 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
13486 * instantiated and should return the decorated service instance. The function is called using
13487 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
13488 * Local injection arguments:
13490 * * `$delegate` - The original service instance, which can be monkey patched, configured,
13491 * decorated or delegated to.
13494 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
13495 * calls to {@link ng.$log#error $log.warn()}.
13497 * $provide.decorator('$log', ['$delegate', function($delegate) {
13498 * $delegate.warn = $delegate.error;
13499 * return $delegate;
13505 function createInjector(modulesToLoad, strictDi) {
13506 strictDi = (strictDi === true);
13507 var INSTANTIATING = {},
13508 providerSuffix = 'Provider',
13510 loadedModules = new HashMap([], true),
13513 provider: supportObject(provider),
13514 factory: supportObject(factory),
13515 service: supportObject(service),
13516 value: supportObject(value),
13517 constant: supportObject(constant),
13518 decorator: decorator
13521 providerInjector = (providerCache.$injector =
13522 createInternalInjector(providerCache, function(serviceName, caller) {
13523 if (angular.isString(caller)) {
13526 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
13528 instanceCache = {},
13529 instanceInjector = (instanceCache.$injector =
13530 createInternalInjector(instanceCache, function(serviceName, caller) {
13531 var provider = providerInjector.get(serviceName + providerSuffix, caller);
13532 return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
13536 forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
13538 return instanceInjector;
13540 ////////////////////////////////////
13542 ////////////////////////////////////
13544 function supportObject(delegate) {
13545 return function(key, value) {
13546 if (isObject(key)) {
13547 forEach(key, reverseParams(delegate));
13549 return delegate(key, value);
13554 function provider(name, provider_) {
13555 assertNotHasOwnProperty(name, 'service');
13556 if (isFunction(provider_) || isArray(provider_)) {
13557 provider_ = providerInjector.instantiate(provider_);
13559 if (!provider_.$get) {
13560 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
13562 return providerCache[name + providerSuffix] = provider_;
13565 function enforceReturnValue(name, factory) {
13566 return function enforcedReturnValue() {
13567 var result = instanceInjector.invoke(factory, this);
13568 if (isUndefined(result)) {
13569 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
13575 function factory(name, factoryFn, enforce) {
13576 return provider(name, {
13577 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
13581 function service(name, constructor) {
13582 return factory(name, ['$injector', function($injector) {
13583 return $injector.instantiate(constructor);
13587 function value(name, val) { return factory(name, valueFn(val), false); }
13589 function constant(name, value) {
13590 assertNotHasOwnProperty(name, 'constant');
13591 providerCache[name] = value;
13592 instanceCache[name] = value;
13595 function decorator(serviceName, decorFn) {
13596 var origProvider = providerInjector.get(serviceName + providerSuffix),
13597 orig$get = origProvider.$get;
13599 origProvider.$get = function() {
13600 var origInstance = instanceInjector.invoke(orig$get, origProvider);
13601 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
13605 ////////////////////////////////////
13607 ////////////////////////////////////
13608 function loadModules(modulesToLoad) {
13609 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
13610 var runBlocks = [], moduleFn;
13611 forEach(modulesToLoad, function(module) {
13612 if (loadedModules.get(module)) return;
13613 loadedModules.put(module, true);
13615 function runInvokeQueue(queue) {
13617 for (i = 0, ii = queue.length; i < ii; i++) {
13618 var invokeArgs = queue[i],
13619 provider = providerInjector.get(invokeArgs[0]);
13621 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
13626 if (isString(module)) {
13627 moduleFn = angularModule(module);
13628 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
13629 runInvokeQueue(moduleFn._invokeQueue);
13630 runInvokeQueue(moduleFn._configBlocks);
13631 } else if (isFunction(module)) {
13632 runBlocks.push(providerInjector.invoke(module));
13633 } else if (isArray(module)) {
13634 runBlocks.push(providerInjector.invoke(module));
13636 assertArgFn(module, 'module');
13639 if (isArray(module)) {
13640 module = module[module.length - 1];
13642 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
13643 // Safari & FF's stack traces don't contain error.message content
13644 // unlike those of Chrome and IE
13645 // So if stack doesn't contain message, we create a new string that contains both.
13646 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
13648 e = e.message + '\n' + e.stack;
13650 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
13651 module, e.stack || e.message || e);
13657 ////////////////////////////////////
13658 // internal Injector
13659 ////////////////////////////////////
13661 function createInternalInjector(cache, factory) {
13663 function getService(serviceName, caller) {
13664 if (cache.hasOwnProperty(serviceName)) {
13665 if (cache[serviceName] === INSTANTIATING) {
13666 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
13667 serviceName + ' <- ' + path.join(' <- '));
13669 return cache[serviceName];
13672 path.unshift(serviceName);
13673 cache[serviceName] = INSTANTIATING;
13674 return cache[serviceName] = factory(serviceName, caller);
13676 if (cache[serviceName] === INSTANTIATING) {
13677 delete cache[serviceName];
13686 function invoke(fn, self, locals, serviceName) {
13687 if (typeof locals === 'string') {
13688 serviceName = locals;
13693 $inject = createInjector.$$annotate(fn, strictDi, serviceName),
13697 for (i = 0, length = $inject.length; i < length; i++) {
13699 if (typeof key !== 'string') {
13700 throw $injectorMinErr('itkn',
13701 'Incorrect injection token! Expected service name as string, got {0}', key);
13704 locals && locals.hasOwnProperty(key)
13706 : getService(key, serviceName)
13713 // http://jsperf.com/angularjs-invoke-apply-vs-switch
13715 return fn.apply(self, args);
13718 function instantiate(Type, locals, serviceName) {
13719 // Check if Type is annotated and use just the given function at n-1 as parameter
13720 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
13721 // Object creation: http://jsperf.com/create-constructor/2
13722 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
13723 var returnedValue = invoke(Type, instance, locals, serviceName);
13725 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
13730 instantiate: instantiate,
13732 annotate: createInjector.$$annotate,
13733 has: function(name) {
13734 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
13740 createInjector.$$annotate = annotate;
13744 * @name $anchorScrollProvider
13747 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
13748 * {@link ng.$location#hash $location.hash()} changes.
13750 function $AnchorScrollProvider() {
13752 var autoScrollingEnabled = true;
13756 * @name $anchorScrollProvider#disableAutoScrolling
13759 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
13760 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
13761 * Use this method to disable automatic scrolling.
13763 * If automatic scrolling is disabled, one must explicitly call
13764 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
13767 this.disableAutoScrolling = function() {
13768 autoScrollingEnabled = false;
13773 * @name $anchorScroll
13775 * @requires $window
13776 * @requires $location
13777 * @requires $rootScope
13780 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
13781 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
13783 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
13785 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
13786 * match any anchor whenever it changes. This can be disabled by calling
13787 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
13789 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
13790 * vertical scroll-offset (either fixed or dynamic).
13792 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
13793 * {@link ng.$location#hash $location.hash()} will be used.
13795 * @property {(number|function|jqLite)} yOffset
13796 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
13797 * positioned elements at the top of the page, such as navbars, headers etc.
13799 * `yOffset` can be specified in various ways:
13800 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
13801 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
13802 * a number representing the offset (in pixels).<br /><br />
13803 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
13804 * the top of the page to the element's bottom will be used as offset.<br />
13805 * **Note**: The element will be taken into account only as long as its `position` is set to
13806 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
13807 * their height and/or positioning according to the viewport's size.
13810 * <div class="alert alert-warning">
13811 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
13812 * not some child element.
13816 <example module="anchorScrollExample">
13817 <file name="index.html">
13818 <div id="scrollArea" ng-controller="ScrollController">
13819 <a ng-click="gotoBottom()">Go to bottom</a>
13820 <a id="bottom"></a> You're at the bottom!
13823 <file name="script.js">
13824 angular.module('anchorScrollExample', [])
13825 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
13826 function ($scope, $location, $anchorScroll) {
13827 $scope.gotoBottom = function() {
13828 // set the location.hash to the id of
13829 // the element you wish to scroll to.
13830 $location.hash('bottom');
13832 // call $anchorScroll()
13837 <file name="style.css">
13845 margin-top: 2000px;
13851 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
13852 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
13855 <example module="anchorScrollOffsetExample">
13856 <file name="index.html">
13857 <div class="fixed-header" ng-controller="headerCtrl">
13858 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
13862 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
13866 <file name="script.js">
13867 angular.module('anchorScrollOffsetExample', [])
13868 .run(['$anchorScroll', function($anchorScroll) {
13869 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
13871 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
13872 function ($anchorScroll, $location, $scope) {
13873 $scope.gotoAnchor = function(x) {
13874 var newHash = 'anchor' + x;
13875 if ($location.hash() !== newHash) {
13876 // set the $location.hash to `newHash` and
13877 // $anchorScroll will automatically scroll to it
13878 $location.hash('anchor' + x);
13880 // call $anchorScroll() explicitly,
13881 // since $location.hash hasn't changed
13888 <file name="style.css">
13894 border: 2px dashed DarkOrchid;
13895 padding: 10px 10px 200px 10px;
13899 background-color: rgba(0, 0, 0, 0.2);
13902 top: 0; left: 0; right: 0;
13905 .fixed-header > a {
13906 display: inline-block;
13912 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
13913 var document = $window.document;
13915 // Helper function to get first anchor from a NodeList
13916 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
13917 // and working in all supported browsers.)
13918 function getFirstAnchor(list) {
13920 Array.prototype.some.call(list, function(element) {
13921 if (nodeName_(element) === 'a') {
13929 function getYOffset() {
13931 var offset = scroll.yOffset;
13933 if (isFunction(offset)) {
13935 } else if (isElement(offset)) {
13936 var elem = offset[0];
13937 var style = $window.getComputedStyle(elem);
13938 if (style.position !== 'fixed') {
13941 offset = elem.getBoundingClientRect().bottom;
13943 } else if (!isNumber(offset)) {
13950 function scrollTo(elem) {
13952 elem.scrollIntoView();
13954 var offset = getYOffset();
13957 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
13958 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
13959 // top of the viewport.
13961 // IF the number of pixels from the top of `elem` to the end of the page's content is less
13962 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
13963 // way down the page.
13965 // This is often the case for elements near the bottom of the page.
13967 // In such cases we do not need to scroll the whole `offset` up, just the difference between
13968 // the top of the element and the offset, which is enough to align the top of `elem` at the
13969 // desired position.
13970 var elemTop = elem.getBoundingClientRect().top;
13971 $window.scrollBy(0, elemTop - offset);
13974 $window.scrollTo(0, 0);
13978 function scroll(hash) {
13979 hash = isString(hash) ? hash : $location.hash();
13982 // empty hash, scroll to the top of the page
13983 if (!hash) scrollTo(null);
13985 // element with given id
13986 else if ((elm = document.getElementById(hash))) scrollTo(elm);
13988 // first anchor with given name :-D
13989 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
13991 // no element and hash == 'top', scroll to the top of the page
13992 else if (hash === 'top') scrollTo(null);
13995 // does not scroll when user clicks on anchor link that is currently on
13996 // (no url change, no $location.hash() change), browser native does scroll
13997 if (autoScrollingEnabled) {
13998 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
13999 function autoScrollWatchAction(newVal, oldVal) {
14000 // skip the initial scroll if $location.hash is empty
14001 if (newVal === oldVal && newVal === '') return;
14003 jqLiteDocumentLoaded(function() {
14004 $rootScope.$evalAsync(scroll);
14013 var $animateMinErr = minErr('$animate');
14014 var ELEMENT_NODE = 1;
14015 var NG_ANIMATE_CLASSNAME = 'ng-animate';
14017 function mergeClasses(a,b) {
14018 if (!a && !b) return '';
14021 if (isArray(a)) a = a.join(' ');
14022 if (isArray(b)) b = b.join(' ');
14023 return a + ' ' + b;
14026 function extractElementNode(element) {
14027 for (var i = 0; i < element.length; i++) {
14028 var elm = element[i];
14029 if (elm.nodeType === ELEMENT_NODE) {
14035 function splitClasses(classes) {
14036 if (isString(classes)) {
14037 classes = classes.split(' ');
14040 // Use createMap() to prevent class assumptions involving property names in
14041 // Object.prototype
14042 var obj = createMap();
14043 forEach(classes, function(klass) {
14044 // sometimes the split leaves empty string values
14045 // incase extra spaces were applied to the options
14046 if (klass.length) {
14053 // if any other type of options value besides an Object value is
14054 // passed into the $animate.method() animation then this helper code
14055 // will be run which will ignore it. While this patch is not the
14056 // greatest solution to this, a lot of existing plugins depend on
14057 // $animate to either call the callback (< 1.2) or return a promise
14058 // that can be changed. This helper function ensures that the options
14059 // are wiped clean incase a callback function is provided.
14060 function prepareAnimateOptions(options) {
14061 return isObject(options)
14066 var $$CoreAnimateRunnerProvider = function() {
14067 this.$get = ['$q', '$$rAF', function($q, $$rAF) {
14068 function AnimateRunner() {}
14069 AnimateRunner.all = noop;
14070 AnimateRunner.chain = noop;
14071 AnimateRunner.prototype = {
14077 then: function(pass, fail) {
14078 return $q(function(resolve) {
14082 }).then(pass, fail);
14085 return AnimateRunner;
14089 // this is prefixed with Core since it conflicts with
14090 // the animateQueueProvider defined in ngAnimate/animateQueue.js
14091 var $$CoreAnimateQueueProvider = function() {
14092 var postDigestQueue = new HashMap();
14093 var postDigestElements = [];
14095 this.$get = ['$$AnimateRunner', '$rootScope',
14096 function($$AnimateRunner, $rootScope) {
14103 push: function(element, event, options, domOperation) {
14104 domOperation && domOperation();
14106 options = options || {};
14107 options.from && element.css(options.from);
14108 options.to && element.css(options.to);
14110 if (options.addClass || options.removeClass) {
14111 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
14114 return new $$AnimateRunner(); // jshint ignore:line
14119 function updateData(data, classes, value) {
14120 var changed = false;
14122 classes = isString(classes) ? classes.split(' ') :
14123 isArray(classes) ? classes : [];
14124 forEach(classes, function(className) {
14127 data[className] = value;
14134 function handleCSSClassChanges() {
14135 forEach(postDigestElements, function(element) {
14136 var data = postDigestQueue.get(element);
14138 var existing = splitClasses(element.attr('class'));
14141 forEach(data, function(status, className) {
14142 var hasClass = !!existing[className];
14143 if (status !== hasClass) {
14145 toAdd += (toAdd.length ? ' ' : '') + className;
14147 toRemove += (toRemove.length ? ' ' : '') + className;
14152 forEach(element, function(elm) {
14153 toAdd && jqLiteAddClass(elm, toAdd);
14154 toRemove && jqLiteRemoveClass(elm, toRemove);
14156 postDigestQueue.remove(element);
14159 postDigestElements.length = 0;
14163 function addRemoveClassesPostDigest(element, add, remove) {
14164 var data = postDigestQueue.get(element) || {};
14166 var classesAdded = updateData(data, add, true);
14167 var classesRemoved = updateData(data, remove, false);
14169 if (classesAdded || classesRemoved) {
14171 postDigestQueue.put(element, data);
14172 postDigestElements.push(element);
14174 if (postDigestElements.length === 1) {
14175 $rootScope.$$postDigest(handleCSSClassChanges);
14184 * @name $animateProvider
14187 * Default implementation of $animate that doesn't perform any animations, instead just
14188 * synchronously performs DOM updates and resolves the returned runner promise.
14190 * In order to enable animations the `ngAnimate` module has to be loaded.
14192 * To see the functional implementation check out `src/ngAnimate/animate.js`.
14194 var $AnimateProvider = ['$provide', function($provide) {
14195 var provider = this;
14197 this.$$registeredAnimations = Object.create(null);
14201 * @name $animateProvider#register
14204 * Registers a new injectable animation factory function. The factory function produces the
14205 * animation object which contains callback functions for each event that is expected to be
14208 * * `eventFn`: `function(element, ... , doneFunction, options)`
14209 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
14210 * on the type of animation additional arguments will be injected into the animation function. The
14211 * list below explains the function signatures for the different animation methods:
14213 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
14214 * - addClass: function(element, addedClasses, doneFunction, options)
14215 * - removeClass: function(element, removedClasses, doneFunction, options)
14216 * - enter, leave, move: function(element, doneFunction, options)
14217 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
14219 * Make sure to trigger the `doneFunction` once the animation is fully complete.
14223 * //enter, leave, move signature
14224 * eventFn : function(element, done, options) {
14225 * //code to run the animation
14226 * //once complete, then run done()
14227 * return function endFunction(wasCancelled) {
14228 * //code to cancel the animation
14234 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
14235 * @param {Function} factory The factory function that will be executed to return the animation
14238 this.register = function(name, factory) {
14239 if (name && name.charAt(0) !== '.') {
14240 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
14243 var key = name + '-animation';
14244 provider.$$registeredAnimations[name.substr(1)] = key;
14245 $provide.factory(key, factory);
14250 * @name $animateProvider#classNameFilter
14253 * Sets and/or returns the CSS class regular expression that is checked when performing
14254 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
14255 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
14256 * When setting the `classNameFilter` value, animations will only be performed on elements
14257 * that successfully match the filter expression. This in turn can boost performance
14258 * for low-powered devices as well as applications containing a lot of structural operations.
14259 * @param {RegExp=} expression The className expression which will be checked against all animations
14260 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
14262 this.classNameFilter = function(expression) {
14263 if (arguments.length === 1) {
14264 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
14265 if (this.$$classNameFilter) {
14266 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
14267 if (reservedRegex.test(this.$$classNameFilter.toString())) {
14268 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
14273 return this.$$classNameFilter;
14276 this.$get = ['$$animateQueue', function($$animateQueue) {
14277 function domInsert(element, parentElement, afterElement) {
14278 // if for some reason the previous element was removed
14279 // from the dom sometime before this code runs then let's
14280 // just stick to using the parent element as the anchor
14281 if (afterElement) {
14282 var afterNode = extractElementNode(afterElement);
14283 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
14284 afterElement = null;
14287 afterElement ? afterElement.after(element) : parentElement.prepend(element);
14293 * @description The $animate service exposes a series of DOM utility methods that provide support
14294 * for animation hooks. The default behavior is the application of DOM operations, however,
14295 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
14296 * to ensure that animation runs with the triggered DOM operation.
14298 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
14299 * included and only when it is active then the animation hooks that `$animate` triggers will be
14300 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
14301 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
14302 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
14304 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
14306 * To learn more about enabling animation support, click here to visit the
14307 * {@link ngAnimate ngAnimate module page}.
14310 // we don't call it directly since non-existant arguments may
14311 // be interpreted as null within the sub enabled function
14316 * @name $animate#on
14318 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
14319 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
14320 * is fired with the following params:
14323 * $animate.on('enter', container,
14324 * function callback(element, phase) {
14325 * // cool we detected an enter animation within the container
14330 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
14331 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
14332 * as well as among its children
14333 * @param {Function} callback the callback function that will be fired when the listener is triggered
14335 * The arguments present in the callback function are:
14336 * * `element` - The captured DOM element that the animation was fired on.
14337 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
14339 on: $$animateQueue.on,
14344 * @name $animate#off
14346 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
14347 * can be used in three different ways depending on the arguments:
14350 * // remove all the animation event listeners listening for `enter`
14351 * $animate.off('enter');
14353 * // remove all the animation event listeners listening for `enter` on the given element and its children
14354 * $animate.off('enter', container);
14356 * // remove the event listener function provided by `listenerFn` that is set
14357 * // to listen for `enter` on the given `element` as well as its children
14358 * $animate.off('enter', container, callback);
14361 * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
14362 * @param {DOMElement=} container the container element the event listener was placed on
14363 * @param {Function=} callback the callback function that was registered as the listener
14365 off: $$animateQueue.off,
14369 * @name $animate#pin
14371 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
14372 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
14373 * element despite being outside the realm of the application or within another application. Say for example if the application
14374 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
14375 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
14376 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
14378 * Note that this feature is only active when the `ngAnimate` module is used.
14380 * @param {DOMElement} element the external element that will be pinned
14381 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
14383 pin: $$animateQueue.pin,
14388 * @name $animate#enabled
14390 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
14391 * function can be called in four ways:
14394 * // returns true or false
14395 * $animate.enabled();
14397 * // changes the enabled state for all animations
14398 * $animate.enabled(false);
14399 * $animate.enabled(true);
14401 * // returns true or false if animations are enabled for an element
14402 * $animate.enabled(element);
14404 * // changes the enabled state for an element and its children
14405 * $animate.enabled(element, true);
14406 * $animate.enabled(element, false);
14409 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
14410 * @param {boolean=} enabled whether or not the animations will be enabled for the element
14412 * @return {boolean} whether or not animations are enabled
14414 enabled: $$animateQueue.enabled,
14418 * @name $animate#cancel
14420 * @description Cancels the provided animation.
14422 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
14424 cancel: function(runner) {
14425 runner.end && runner.end();
14431 * @name $animate#enter
14433 * @description Inserts the element into the DOM either after the `after` element (if provided) or
14434 * as the first child within the `parent` element and then triggers an animation.
14435 * A promise is returned that will be resolved during the next digest once the animation
14438 * @param {DOMElement} element the element which will be inserted into the DOM
14439 * @param {DOMElement} parent the parent element which will append the element as
14440 * a child (so long as the after element is not present)
14441 * @param {DOMElement=} after the sibling element after which the element will be appended
14442 * @param {object=} options an optional collection of options/styles that will be applied to the element
14444 * @return {Promise} the animation callback promise
14446 enter: function(element, parent, after, options) {
14447 parent = parent && jqLite(parent);
14448 after = after && jqLite(after);
14449 parent = parent || after.parent();
14450 domInsert(element, parent, after);
14451 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
14457 * @name $animate#move
14459 * @description Inserts (moves) the element into its new position in the DOM either after
14460 * the `after` element (if provided) or as the first child within the `parent` element
14461 * and then triggers an animation. A promise is returned that will be resolved
14462 * during the next digest once the animation has completed.
14464 * @param {DOMElement} element the element which will be moved into the new DOM position
14465 * @param {DOMElement} parent the parent element which will append the element as
14466 * a child (so long as the after element is not present)
14467 * @param {DOMElement=} after the sibling element after which the element will be appended
14468 * @param {object=} options an optional collection of options/styles that will be applied to the element
14470 * @return {Promise} the animation callback promise
14472 move: function(element, parent, after, options) {
14473 parent = parent && jqLite(parent);
14474 after = after && jqLite(after);
14475 parent = parent || after.parent();
14476 domInsert(element, parent, after);
14477 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
14482 * @name $animate#leave
14484 * @description Triggers an animation and then removes the element from the DOM.
14485 * When the function is called a promise is returned that will be resolved during the next
14486 * digest once the animation has completed.
14488 * @param {DOMElement} element the element which will be removed from the DOM
14489 * @param {object=} options an optional collection of options/styles that will be applied to the element
14491 * @return {Promise} the animation callback promise
14493 leave: function(element, options) {
14494 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
14501 * @name $animate#addClass
14504 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
14505 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
14506 * animation if element already contains the CSS class or if the class is removed at a later step.
14507 * Note that class-based animations are treated differently compared to structural animations
14508 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14509 * depending if CSS or JavaScript animations are used.
14511 * @param {DOMElement} element the element which the CSS classes will be applied to
14512 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
14513 * @param {object=} options an optional collection of options/styles that will be applied to the element
14515 * @return {Promise} the animation callback promise
14517 addClass: function(element, className, options) {
14518 options = prepareAnimateOptions(options);
14519 options.addClass = mergeClasses(options.addclass, className);
14520 return $$animateQueue.push(element, 'addClass', options);
14525 * @name $animate#removeClass
14528 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
14529 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
14530 * animation if element does not contain the CSS class or if the class is added at a later step.
14531 * Note that class-based animations are treated differently compared to structural animations
14532 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14533 * depending if CSS or JavaScript animations are used.
14535 * @param {DOMElement} element the element which the CSS classes will be applied to
14536 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
14537 * @param {object=} options an optional collection of options/styles that will be applied to the element
14539 * @return {Promise} the animation callback promise
14541 removeClass: function(element, className, options) {
14542 options = prepareAnimateOptions(options);
14543 options.removeClass = mergeClasses(options.removeClass, className);
14544 return $$animateQueue.push(element, 'removeClass', options);
14549 * @name $animate#setClass
14552 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
14553 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
14554 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
14555 * passed. Note that class-based animations are treated differently compared to structural animations
14556 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14557 * depending if CSS or JavaScript animations are used.
14559 * @param {DOMElement} element the element which the CSS classes will be applied to
14560 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
14561 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
14562 * @param {object=} options an optional collection of options/styles that will be applied to the element
14564 * @return {Promise} the animation callback promise
14566 setClass: function(element, add, remove, options) {
14567 options = prepareAnimateOptions(options);
14568 options.addClass = mergeClasses(options.addClass, add);
14569 options.removeClass = mergeClasses(options.removeClass, remove);
14570 return $$animateQueue.push(element, 'setClass', options);
14575 * @name $animate#animate
14578 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
14579 * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
14580 * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
14581 * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
14582 * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
14584 * @param {DOMElement} element the element which the CSS styles will be applied to
14585 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
14586 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
14587 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
14588 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
14589 * (Note that if no animation is detected then this value will not be appplied to the element.)
14590 * @param {object=} options an optional collection of options/styles that will be applied to the element
14592 * @return {Promise} the animation callback promise
14594 animate: function(element, from, to, className, options) {
14595 options = prepareAnimateOptions(options);
14596 options.from = options.from ? extend(options.from, from) : from;
14597 options.to = options.to ? extend(options.to, to) : to;
14599 className = className || 'ng-inline-animate';
14600 options.tempClasses = mergeClasses(options.tempClasses, className);
14601 return $$animateQueue.push(element, 'animate', options);
14609 * @name $animateCss
14613 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
14614 * then the `$animateCss` service will actually perform animations.
14616 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
14618 var $CoreAnimateCssProvider = function() {
14619 this.$get = ['$$rAF', '$q', function($$rAF, $q) {
14621 var RAFPromise = function() {};
14622 RAFPromise.prototype = {
14623 done: function(cancel) {
14624 this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
14629 cancel: function() {
14632 getPromise: function() {
14634 this.defer = $q.defer();
14636 return this.defer.promise;
14638 then: function(f1,f2) {
14639 return this.getPromise().then(f1,f2);
14641 'catch': function(f1) {
14642 return this.getPromise()['catch'](f1);
14644 'finally': function(f1) {
14645 return this.getPromise()['finally'](f1);
14649 return function(element, options) {
14650 // there is no point in applying the styles since
14651 // there is no animation that goes on at all in
14652 // this version of $animateCss.
14653 if (options.cleanupStyles) {
14654 options.from = options.to = null;
14657 if (options.from) {
14658 element.css(options.from);
14659 options.from = null;
14662 var closed, runner = new RAFPromise();
14680 if (options.addClass) {
14681 element.addClass(options.addClass);
14682 options.addClass = null;
14684 if (options.removeClass) {
14685 element.removeClass(options.removeClass);
14686 options.removeClass = null;
14689 element.css(options.to);
14697 /* global stripHash: true */
14700 * ! This is a private undocumented service !
14705 * This object has two goals:
14707 * - hide all the global state in the browser caused by the window object
14708 * - abstract away all the browser specific features and inconsistencies
14710 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
14711 * service, which can be used for convenient testing of the application without the interaction with
14712 * the real browser apis.
14715 * @param {object} window The global window object.
14716 * @param {object} document jQuery wrapped document.
14717 * @param {object} $log window.console or an object with the same interface.
14718 * @param {object} $sniffer $sniffer service
14720 function Browser(window, document, $log, $sniffer) {
14722 rawDocument = document[0],
14723 location = window.location,
14724 history = window.history,
14725 setTimeout = window.setTimeout,
14726 clearTimeout = window.clearTimeout,
14727 pendingDeferIds = {};
14729 self.isMock = false;
14731 var outstandingRequestCount = 0;
14732 var outstandingRequestCallbacks = [];
14734 // TODO(vojta): remove this temporary api
14735 self.$$completeOutstandingRequest = completeOutstandingRequest;
14736 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
14739 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
14740 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
14742 function completeOutstandingRequest(fn) {
14744 fn.apply(null, sliceArgs(arguments, 1));
14746 outstandingRequestCount--;
14747 if (outstandingRequestCount === 0) {
14748 while (outstandingRequestCallbacks.length) {
14750 outstandingRequestCallbacks.pop()();
14759 function getHash(url) {
14760 var index = url.indexOf('#');
14761 return index === -1 ? '' : url.substr(index);
14766 * Note: this method is used only by scenario runner
14767 * TODO(vojta): prefix this method with $$ ?
14768 * @param {function()} callback Function that will be called when no outstanding request
14770 self.notifyWhenNoOutstandingRequests = function(callback) {
14771 if (outstandingRequestCount === 0) {
14774 outstandingRequestCallbacks.push(callback);
14778 //////////////////////////////////////////////////////////////
14780 //////////////////////////////////////////////////////////////
14782 var cachedState, lastHistoryState,
14783 lastBrowserUrl = location.href,
14784 baseElement = document.find('base'),
14785 pendingLocation = null;
14788 lastHistoryState = cachedState;
14791 * @name $browser#url
14795 * Without any argument, this method just returns current value of location.href.
14798 * With at least one argument, this method sets url to new value.
14799 * If html5 history api supported, pushState/replaceState is used, otherwise
14800 * location.href/location.replace is used.
14801 * Returns its own instance to allow chaining
14803 * NOTE: this api is intended for use only by the $location service. Please use the
14804 * {@link ng.$location $location service} to change url.
14806 * @param {string} url New url (when used as setter)
14807 * @param {boolean=} replace Should new url replace current history record?
14808 * @param {object=} state object to use with pushState/replaceState
14810 self.url = function(url, replace, state) {
14811 // In modern browsers `history.state` is `null` by default; treating it separately
14812 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
14813 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
14814 if (isUndefined(state)) {
14818 // Android Browser BFCache causes location, history reference to become stale.
14819 if (location !== window.location) location = window.location;
14820 if (history !== window.history) history = window.history;
14824 var sameState = lastHistoryState === state;
14826 // Don't change anything if previous and current URLs and states match. This also prevents
14827 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
14828 // See https://github.com/angular/angular.js/commit/ffb2701
14829 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
14832 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
14833 lastBrowserUrl = url;
14834 lastHistoryState = state;
14835 // Don't use history API if only the hash changed
14836 // due to a bug in IE10/IE11 which leads
14837 // to not firing a `hashchange` nor `popstate` event
14838 // in some cases (see #9143).
14839 if ($sniffer.history && (!sameBase || !sameState)) {
14840 history[replace ? 'replaceState' : 'pushState'](state, '', url);
14842 // Do the assignment again so that those two variables are referentially identical.
14843 lastHistoryState = cachedState;
14845 if (!sameBase || pendingLocation) {
14846 pendingLocation = url;
14849 location.replace(url);
14850 } else if (!sameBase) {
14851 location.href = url;
14853 location.hash = getHash(url);
14855 if (location.href !== url) {
14856 pendingLocation = url;
14862 // - pendingLocation is needed as browsers don't allow to read out
14863 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
14864 // https://openradar.appspot.com/22186109).
14865 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
14866 return pendingLocation || location.href.replace(/%27/g,"'");
14871 * @name $browser#state
14874 * This method is a getter.
14876 * Return history.state or null if history.state is undefined.
14878 * @returns {object} state
14880 self.state = function() {
14881 return cachedState;
14884 var urlChangeListeners = [],
14885 urlChangeInit = false;
14887 function cacheStateAndFireUrlChange() {
14888 pendingLocation = null;
14893 function getCurrentState() {
14895 return history.state;
14897 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
14901 // This variable should be used *only* inside the cacheState function.
14902 var lastCachedState = null;
14903 function cacheState() {
14904 // This should be the only place in $browser where `history.state` is read.
14905 cachedState = getCurrentState();
14906 cachedState = isUndefined(cachedState) ? null : cachedState;
14908 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
14909 if (equals(cachedState, lastCachedState)) {
14910 cachedState = lastCachedState;
14912 lastCachedState = cachedState;
14915 function fireUrlChange() {
14916 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
14920 lastBrowserUrl = self.url();
14921 lastHistoryState = cachedState;
14922 forEach(urlChangeListeners, function(listener) {
14923 listener(self.url(), cachedState);
14928 * @name $browser#onUrlChange
14931 * Register callback function that will be called, when url changes.
14933 * It's only called when the url is changed from outside of angular:
14934 * - user types different url into address bar
14935 * - user clicks on history (forward/back) button
14936 * - user clicks on a link
14938 * It's not called when url is changed by $browser.url() method
14940 * The listener gets called with new url as parameter.
14942 * NOTE: this api is intended for use only by the $location service. Please use the
14943 * {@link ng.$location $location service} to monitor url changes in angular apps.
14945 * @param {function(string)} listener Listener function to be called when url changes.
14946 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
14948 self.onUrlChange = function(callback) {
14949 // TODO(vojta): refactor to use node's syntax for events
14950 if (!urlChangeInit) {
14951 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
14952 // don't fire popstate when user change the address bar and don't fire hashchange when url
14953 // changed by push/replaceState
14955 // html5 history api - popstate event
14956 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
14957 // hashchange event
14958 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
14960 urlChangeInit = true;
14963 urlChangeListeners.push(callback);
14969 * Remove popstate and hashchange handler from window.
14971 * NOTE: this api is intended for use only by $rootScope.
14973 self.$$applicationDestroyed = function() {
14974 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
14978 * Checks whether the url has changed outside of Angular.
14979 * Needs to be exported to be able to check for changes that have been done in sync,
14980 * as hashchange/popstate events fire in async.
14982 self.$$checkUrlChange = fireUrlChange;
14984 //////////////////////////////////////////////////////////////
14986 //////////////////////////////////////////////////////////////
14989 * @name $browser#baseHref
14992 * Returns current <base href>
14993 * (always relative - without domain)
14995 * @returns {string} The current base href
14997 self.baseHref = function() {
14998 var href = baseElement.attr('href');
14999 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
15003 * @name $browser#defer
15004 * @param {function()} fn A function, who's execution should be deferred.
15005 * @param {number=} [delay=0] of milliseconds to defer the function execution.
15006 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
15009 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
15011 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
15012 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
15013 * via `$browser.defer.flush()`.
15016 self.defer = function(fn, delay) {
15018 outstandingRequestCount++;
15019 timeoutId = setTimeout(function() {
15020 delete pendingDeferIds[timeoutId];
15021 completeOutstandingRequest(fn);
15023 pendingDeferIds[timeoutId] = true;
15029 * @name $browser#defer.cancel
15032 * Cancels a deferred task identified with `deferId`.
15034 * @param {*} deferId Token returned by the `$browser.defer` function.
15035 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
15038 self.defer.cancel = function(deferId) {
15039 if (pendingDeferIds[deferId]) {
15040 delete pendingDeferIds[deferId];
15041 clearTimeout(deferId);
15042 completeOutstandingRequest(noop);
15050 function $BrowserProvider() {
15051 this.$get = ['$window', '$log', '$sniffer', '$document',
15052 function($window, $log, $sniffer, $document) {
15053 return new Browser($window, $document, $log, $sniffer);
15059 * @name $cacheFactory
15062 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
15067 * var cache = $cacheFactory('cacheId');
15068 * expect($cacheFactory.get('cacheId')).toBe(cache);
15069 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
15071 * cache.put("key", "value");
15072 * cache.put("another key", "another value");
15074 * // We've specified no options on creation
15075 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
15080 * @param {string} cacheId Name or id of the newly created cache.
15081 * @param {object=} options Options object that specifies the cache behavior. Properties:
15083 * - `{number=}` `capacity` — turns the cache into LRU cache.
15085 * @returns {object} Newly created cache object with the following set of methods:
15087 * - `{object}` `info()` — Returns id, size, and options of cache.
15088 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
15090 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
15091 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
15092 * - `{void}` `removeAll()` — Removes all cached values.
15093 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
15096 <example module="cacheExampleApp">
15097 <file name="index.html">
15098 <div ng-controller="CacheController">
15099 <input ng-model="newCacheKey" placeholder="Key">
15100 <input ng-model="newCacheValue" placeholder="Value">
15101 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
15103 <p ng-if="keys.length">Cached Values</p>
15104 <div ng-repeat="key in keys">
15105 <span ng-bind="key"></span>
15107 <b ng-bind="cache.get(key)"></b>
15111 <div ng-repeat="(key, value) in cache.info()">
15112 <span ng-bind="key"></span>
15114 <b ng-bind="value"></b>
15118 <file name="script.js">
15119 angular.module('cacheExampleApp', []).
15120 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
15122 $scope.cache = $cacheFactory('cacheId');
15123 $scope.put = function(key, value) {
15124 if (angular.isUndefined($scope.cache.get(key))) {
15125 $scope.keys.push(key);
15127 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
15131 <file name="style.css">
15133 margin: 10px 0 3px;
15138 function $CacheFactoryProvider() {
15140 this.$get = function() {
15143 function cacheFactory(cacheId, options) {
15144 if (cacheId in caches) {
15145 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
15149 stats = extend({}, options, {id: cacheId}),
15150 data = createMap(),
15151 capacity = (options && options.capacity) || Number.MAX_VALUE,
15152 lruHash = createMap(),
15158 * @name $cacheFactory.Cache
15161 * A cache object used to store and retrieve data, primarily used by
15162 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
15163 * templates and other data.
15166 * angular.module('superCache')
15167 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
15168 * return $cacheFactory('super-cache');
15175 * it('should behave like a cache', inject(function(superCache) {
15176 * superCache.put('key', 'value');
15177 * superCache.put('another key', 'another value');
15179 * expect(superCache.info()).toEqual({
15180 * id: 'super-cache',
15184 * superCache.remove('another key');
15185 * expect(superCache.get('another key')).toBeUndefined();
15187 * superCache.removeAll();
15188 * expect(superCache.info()).toEqual({
15189 * id: 'super-cache',
15195 return caches[cacheId] = {
15199 * @name $cacheFactory.Cache#put
15203 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
15204 * retrieved later, and incrementing the size of the cache if the key was not already
15205 * present in the cache. If behaving like an LRU cache, it will also remove stale
15206 * entries from the set.
15208 * It will not insert undefined values into the cache.
15210 * @param {string} key the key under which the cached data is stored.
15211 * @param {*} value the value to store alongside the key. If it is undefined, the key
15212 * will not be stored.
15213 * @returns {*} the value stored.
15215 put: function(key, value) {
15216 if (isUndefined(value)) return;
15217 if (capacity < Number.MAX_VALUE) {
15218 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
15223 if (!(key in data)) size++;
15226 if (size > capacity) {
15227 this.remove(staleEnd.key);
15235 * @name $cacheFactory.Cache#get
15239 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
15241 * @param {string} key the key of the data to be retrieved
15242 * @returns {*} the value stored.
15244 get: function(key) {
15245 if (capacity < Number.MAX_VALUE) {
15246 var lruEntry = lruHash[key];
15248 if (!lruEntry) return;
15259 * @name $cacheFactory.Cache#remove
15263 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
15265 * @param {string} key the key of the entry to be removed
15267 remove: function(key) {
15268 if (capacity < Number.MAX_VALUE) {
15269 var lruEntry = lruHash[key];
15271 if (!lruEntry) return;
15273 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
15274 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
15275 link(lruEntry.n,lruEntry.p);
15277 delete lruHash[key];
15280 if (!(key in data)) return;
15289 * @name $cacheFactory.Cache#removeAll
15293 * Clears the cache object of any entries.
15295 removeAll: function() {
15296 data = createMap();
15298 lruHash = createMap();
15299 freshEnd = staleEnd = null;
15305 * @name $cacheFactory.Cache#destroy
15309 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
15310 * removing it from the {@link $cacheFactory $cacheFactory} set.
15312 destroy: function() {
15316 delete caches[cacheId];
15322 * @name $cacheFactory.Cache#info
15326 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
15328 * @returns {object} an object with the following properties:
15330 * <li>**id**: the id of the cache instance</li>
15331 * <li>**size**: the number of entries kept in the cache instance</li>
15332 * <li>**...**: any additional properties from the options object when creating the
15337 return extend({}, stats, {size: size});
15343 * makes the `entry` the freshEnd of the LRU linked list
15345 function refresh(entry) {
15346 if (entry != freshEnd) {
15349 } else if (staleEnd == entry) {
15350 staleEnd = entry.n;
15353 link(entry.n, entry.p);
15354 link(entry, freshEnd);
15362 * bidirectionally links two entries of the LRU linked list
15364 function link(nextEntry, prevEntry) {
15365 if (nextEntry != prevEntry) {
15366 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
15367 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
15375 * @name $cacheFactory#info
15378 * Get information about all the caches that have been created
15380 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
15382 cacheFactory.info = function() {
15384 forEach(caches, function(cache, cacheId) {
15385 info[cacheId] = cache.info();
15393 * @name $cacheFactory#get
15396 * Get access to a cache object by the `cacheId` used when it was created.
15398 * @param {string} cacheId Name or id of a cache to access.
15399 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
15401 cacheFactory.get = function(cacheId) {
15402 return caches[cacheId];
15406 return cacheFactory;
15412 * @name $templateCache
15415 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
15416 * can load templates directly into the cache in a `script` tag, or by consuming the
15417 * `$templateCache` service directly.
15419 * Adding via the `script` tag:
15422 * <script type="text/ng-template" id="templateId.html">
15423 * <p>This is the content of the template</p>
15427 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
15428 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
15429 * element with ng-app attribute), otherwise the template will be ignored.
15431 * Adding via the `$templateCache` service:
15434 * var myApp = angular.module('myApp', []);
15435 * myApp.run(function($templateCache) {
15436 * $templateCache.put('templateId.html', 'This is the content of the template');
15440 * To retrieve the template later, simply use it in your HTML:
15442 * <div ng-include=" 'templateId.html' "></div>
15445 * or get it via Javascript:
15447 * $templateCache.get('templateId.html')
15450 * See {@link ng.$cacheFactory $cacheFactory}.
15453 function $TemplateCacheProvider() {
15454 this.$get = ['$cacheFactory', function($cacheFactory) {
15455 return $cacheFactory('templates');
15459 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15460 * Any commits to this file should be reviewed with security in mind. *
15461 * Changes to this file can potentially create security vulnerabilities. *
15462 * An approval from 2 Core members with history of modifying *
15463 * this file is required. *
15465 * Does the change somehow allow for arbitrary javascript to be executed? *
15466 * Or allows for someone to change the prototype of built-in objects? *
15467 * Or gives undesired access to variables likes document or window? *
15468 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15470 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
15472 * DOM-related variables:
15474 * - "node" - DOM Node
15475 * - "element" - DOM Element or Node
15476 * - "$node" or "$element" - jqLite-wrapped node or element
15479 * Compiler related stuff:
15481 * - "linkFn" - linking fn of a single directive
15482 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
15483 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
15484 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
15494 * Compiles an HTML string or DOM into a template and produces a template function, which
15495 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
15497 * The compilation is a process of walking the DOM tree and matching DOM elements to
15498 * {@link ng.$compileProvider#directive directives}.
15500 * <div class="alert alert-warning">
15501 * **Note:** This document is an in-depth reference of all directive options.
15502 * For a gentle introduction to directives with examples of common use cases,
15503 * see the {@link guide/directive directive guide}.
15506 * ## Comprehensive Directive API
15508 * There are many different options for a directive.
15510 * The difference resides in the return value of the factory function.
15511 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
15512 * or just the `postLink` function (all other properties will have the default values).
15514 * <div class="alert alert-success">
15515 * **Best Practice:** It's recommended to use the "directive definition object" form.
15518 * Here's an example directive declared with a Directive Definition Object:
15521 * var myModule = angular.module(...);
15523 * myModule.directive('directiveName', function factory(injectables) {
15524 * var directiveDefinitionObject = {
15526 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
15528 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
15529 * transclude: false,
15531 * templateNamespace: 'html',
15533 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
15534 * controllerAs: 'stringIdentifier',
15535 * bindToController: false,
15536 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
15537 * compile: function compile(tElement, tAttrs, transclude) {
15539 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
15540 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
15543 * // return function postLink( ... ) { ... }
15547 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
15548 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
15551 * // link: function postLink( ... ) { ... }
15553 * return directiveDefinitionObject;
15557 * <div class="alert alert-warning">
15558 * **Note:** Any unspecified options will use the default value. You can see the default values below.
15561 * Therefore the above can be simplified as:
15564 * var myModule = angular.module(...);
15566 * myModule.directive('directiveName', function factory(injectables) {
15567 * var directiveDefinitionObject = {
15568 * link: function postLink(scope, iElement, iAttrs) { ... }
15570 * return directiveDefinitionObject;
15572 * // return function postLink(scope, iElement, iAttrs) { ... }
15578 * ### Directive Definition Object
15580 * The directive definition object provides instructions to the {@link ng.$compile
15581 * compiler}. The attributes are:
15583 * #### `multiElement`
15584 * When this property is set to true, the HTML compiler will collect DOM nodes between
15585 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
15586 * together as the directive elements. It is recommended that this feature be used on directives
15587 * which are not strictly behavioural (such as {@link ngClick}), and which
15588 * do not manipulate or replace child nodes (such as {@link ngInclude}).
15591 * When there are multiple directives defined on a single DOM element, sometimes it
15592 * is necessary to specify the order in which the directives are applied. The `priority` is used
15593 * to sort the directives before their `compile` functions get called. Priority is defined as a
15594 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
15595 * are also run in priority order, but post-link functions are run in reverse order. The order
15596 * of directives with the same priority is undefined. The default priority is `0`.
15599 * If set to true then the current `priority` will be the last set of directives
15600 * which will execute (any directives at the current priority will still execute
15601 * as the order of execution on same `priority` is undefined). Note that expressions
15602 * and other directives used in the directive's template will also be excluded from execution.
15605 * The scope property can be `true`, an object or a falsy value:
15607 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
15609 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
15610 * the directive's element. If multiple directives on the same element request a new scope,
15611 * only one new scope is created. The new scope rule does not apply for the root of the template
15612 * since the root of the template always gets a new scope.
15614 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
15615 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
15616 * scope. This is useful when creating reusable components, which should not accidentally read or modify
15617 * data in the parent scope.
15619 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
15620 * directive's element. These local properties are useful for aliasing values for templates. The keys in
15621 * the object hash map to the name of the property on the isolate scope; the values define how the property
15622 * is bound to the parent scope, via matching attributes on the directive's element:
15624 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
15625 * always a string since DOM attributes are strings. If no `attr` name is specified then the
15626 * attribute name is assumed to be the same as the local name.
15627 * Given `<widget my-attr="hello {{name}}">` and widget definition
15628 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
15629 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
15630 * `localName` property on the widget scope. The `name` is read from the parent scope (not
15631 * component scope).
15633 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
15634 * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
15635 * name is specified then the attribute name is assumed to be the same as the local name.
15636 * Given `<widget my-attr="parentModel">` and widget definition of
15637 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
15638 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
15639 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
15640 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
15641 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
15642 * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
15643 * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
15645 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
15646 * If no `attr` name is specified then the attribute name is assumed to be the same as the
15647 * local name. Given `<widget my-attr="count = count + value">` and widget definition of
15648 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
15649 * a function wrapper for the `count = count + value` expression. Often it's desirable to
15650 * pass data from the isolated scope via an expression to the parent scope, this can be
15651 * done by passing a map of local variable names and values into the expression wrapper fn.
15652 * For example, if the expression is `increment(amount)` then we can specify the amount value
15653 * by calling the `localFn` as `localFn({amount: 22})`.
15655 * In general it's possible to apply more than one directive to one element, but there might be limitations
15656 * depending on the type of scope required by the directives. The following points will help explain these limitations.
15657 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
15659 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
15660 * * **child scope** + **no scope** => Both directives will share one single child scope
15661 * * **child scope** + **child scope** => Both directives will share one single child scope
15662 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
15663 * its parent's scope
15664 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
15665 * be applied to the same element.
15666 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
15667 * cannot be applied to the same element.
15670 * #### `bindToController`
15671 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
15672 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
15673 * is instantiated, the initial values of the isolate scope bindings are already available.
15675 * #### `controller`
15676 * Controller constructor function. The controller is instantiated before the
15677 * pre-linking phase and can be accessed by other directives (see
15678 * `require` attribute). This allows the directives to communicate with each other and augment
15679 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
15681 * * `$scope` - Current scope associated with the element
15682 * * `$element` - Current element
15683 * * `$attrs` - Current attributes object for the element
15684 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
15685 * `function([scope], cloneLinkingFn, futureParentElement)`.
15686 * * `scope`: optional argument to override the scope.
15687 * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
15688 * * `futureParentElement`:
15689 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
15690 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
15691 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
15692 * and when the `cloneLinkinFn` is passed,
15693 * as those elements need to created and cloned in a special way when they are defined outside their
15694 * usual containers (e.g. like `<svg>`).
15695 * * See also the `directive.templateNamespace` property.
15699 * Require another directive and inject its controller as the fourth argument to the linking function. The
15700 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
15701 * injected argument will be an array in corresponding order. If no such directive can be
15702 * found, or if the directive does not have a controller, then an error is raised (unless no link function
15703 * is specified, in which case error checking is skipped). The name can be prefixed with:
15705 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
15706 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
15707 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
15708 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
15709 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
15710 * `null` to the `link` fn if not found.
15711 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
15712 * `null` to the `link` fn if not found.
15715 * #### `controllerAs`
15716 * Identifier name for a reference to the controller in the directive's scope.
15717 * This allows the controller to be referenced from the directive template. This is especially
15718 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
15719 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
15720 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
15724 * String of subset of `EACM` which restricts the directive to a specific directive
15725 * declaration style. If omitted, the defaults (elements and attributes) are used.
15727 * * `E` - Element name (default): `<my-directive></my-directive>`
15728 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
15729 * * `C` - Class: `<div class="my-directive: exp;"></div>`
15730 * * `M` - Comment: `<!-- directive: my-directive exp -->`
15733 * #### `templateNamespace`
15734 * String representing the document type used by the markup in the template.
15735 * AngularJS needs this information as those elements need to be created and cloned
15736 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
15738 * * `html` - All root nodes in the template are HTML. Root nodes may also be
15739 * top-level elements such as `<svg>` or `<math>`.
15740 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
15741 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
15743 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
15746 * HTML markup that may:
15747 * * Replace the contents of the directive's element (default).
15748 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
15749 * * Wrap the contents of the directive's element (if `transclude` is true).
15753 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
15754 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
15755 * function api below) and returns a string value.
15758 * #### `templateUrl`
15759 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
15761 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
15762 * for later when the template has been resolved. In the meantime it will continue to compile and link
15763 * sibling and parent elements as though this element had not contained any directives.
15765 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
15766 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
15767 * case when only one deeply nested directive has `templateUrl`.
15769 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
15771 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
15772 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
15773 * a string value representing the url. In either case, the template URL is passed through {@link
15774 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
15777 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
15778 * specify what the template should replace. Defaults to `false`.
15780 * * `true` - the template will replace the directive's element.
15781 * * `false` - the template will replace the contents of the directive's element.
15783 * The replacement process migrates all of the attributes / classes from the old element to the new
15784 * one. See the {@link guide/directive#template-expanding-directive
15785 * Directives Guide} for an example.
15787 * There are very few scenarios where element replacement is required for the application function,
15788 * the main one being reusable custom components that are used within SVG contexts
15789 * (because SVG doesn't work with custom elements in the DOM tree).
15791 * #### `transclude`
15792 * Extract the contents of the element where the directive appears and make it available to the directive.
15793 * The contents are compiled and provided to the directive as a **transclusion function**. See the
15794 * {@link $compile#transclusion Transclusion} section below.
15796 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
15797 * directive's element or the entire element:
15799 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
15800 * * `'element'` - transclude the whole of the directive's element including any directives on this
15801 * element that defined at a lower priority than this directive. When used, the `template`
15802 * property is ignored.
15808 * function compile(tElement, tAttrs, transclude) { ... }
15811 * The compile function deals with transforming the template DOM. Since most directives do not do
15812 * template transformation, it is not used often. The compile function takes the following arguments:
15814 * * `tElement` - template element - The element where the directive has been declared. It is
15815 * safe to do template transformation on the element and child elements only.
15817 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
15818 * between all directive compile functions.
15820 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
15822 * <div class="alert alert-warning">
15823 * **Note:** The template instance and the link instance may be different objects if the template has
15824 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
15825 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
15826 * should be done in a linking function rather than in a compile function.
15829 * <div class="alert alert-warning">
15830 * **Note:** The compile function cannot handle directives that recursively use themselves in their
15831 * own templates or compile functions. Compiling these directives results in an infinite loop and a
15832 * stack overflow errors.
15834 * This can be avoided by manually using $compile in the postLink function to imperatively compile
15835 * a directive's template instead of relying on automatic template compilation via `template` or
15836 * `templateUrl` declaration or manual compilation inside the compile function.
15839 * <div class="alert alert-danger">
15840 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
15841 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
15842 * to the link function instead.
15845 * A compile function can have a return value which can be either a function or an object.
15847 * * returning a (post-link) function - is equivalent to registering the linking function via the
15848 * `link` property of the config object when the compile function is empty.
15850 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
15851 * control when a linking function should be called during the linking phase. See info about
15852 * pre-linking and post-linking functions below.
15856 * This property is used only if the `compile` property is not defined.
15859 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
15862 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
15863 * executed after the template has been cloned. This is where most of the directive logic will be
15866 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
15867 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
15869 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
15870 * manipulate the children of the element only in `postLink` function since the children have
15871 * already been linked.
15873 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
15874 * between all directive linking functions.
15876 * * `controller` - the directive's required controller instance(s) - Instances are shared
15877 * among all directives, which allows the directives to use the controllers as a communication
15878 * channel. The exact value depends on the directive's `require` property:
15879 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
15880 * * `string`: the controller instance
15881 * * `array`: array of controller instances
15883 * If a required controller cannot be found, and it is optional, the instance is `null`,
15884 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
15886 * Note that you can also require the directive's own controller - it will be made available like
15887 * any other controller.
15889 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
15890 * This is the same as the `$transclude`
15891 * parameter of directive controllers, see there for details.
15892 * `function([scope], cloneLinkingFn, futureParentElement)`.
15894 * #### Pre-linking function
15896 * Executed before the child elements are linked. Not safe to do DOM transformation since the
15897 * compiler linking function will fail to locate the correct elements for linking.
15899 * #### Post-linking function
15901 * Executed after the child elements are linked.
15903 * Note that child elements that contain `templateUrl` directives will not have been compiled
15904 * and linked since they are waiting for their template to load asynchronously and their own
15905 * compilation and linking has been suspended until that occurs.
15907 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
15908 * for their async templates to be resolved.
15913 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
15914 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
15915 * scope from where they were taken.
15917 * Transclusion is used (often with {@link ngTransclude}) to insert the
15918 * original contents of a directive's element into a specified place in the template of the directive.
15919 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
15920 * content has access to the properties on the scope from which it was taken, even if the directive
15921 * has isolated scope.
15922 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
15924 * This makes it possible for the widget to have private state for its template, while the transcluded
15925 * content has access to its originating scope.
15927 * <div class="alert alert-warning">
15928 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
15929 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
15930 * Testing Transclusion Directives}.
15933 * #### Transclusion Functions
15935 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
15936 * function** to the directive's `link` function and `controller`. This transclusion function is a special
15937 * **linking function** that will return the compiled contents linked to a new transclusion scope.
15939 * <div class="alert alert-info">
15940 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
15941 * ngTransclude will deal with it for us.
15944 * If you want to manually control the insertion and removal of the transcluded content in your directive
15945 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
15946 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
15948 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
15949 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
15950 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
15952 * <div class="alert alert-info">
15953 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
15954 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
15957 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
15958 * attach function**:
15961 * var transcludedContent, transclusionScope;
15963 * $transclude(function(clone, scope) {
15964 * element.append(clone);
15965 * transcludedContent = clone;
15966 * transclusionScope = scope;
15970 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
15971 * associated transclusion scope:
15974 * transcludedContent.remove();
15975 * transclusionScope.$destroy();
15978 * <div class="alert alert-info">
15979 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
15980 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
15981 * then you are also responsible for calling `$destroy` on the transclusion scope.
15984 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
15985 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
15986 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
15989 * #### Transclusion Scopes
15991 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
15992 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
15993 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
15996 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
16002 * <div transclusion>
16008 * The `$parent` scope hierarchy will look like this:
16016 * but the scopes will inherit prototypically from different scopes to their `$parent`.
16027 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
16028 * `link()` or `compile()` functions. It has a variety of uses.
16030 * accessing *Normalized attribute names:*
16031 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
16032 * the attributes object allows for normalized access to
16035 * * *Directive inter-communication:* All directives share the same instance of the attributes
16036 * object which allows the directives to use the attributes object as inter directive
16039 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
16040 * allowing other directives to read the interpolated value.
16042 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
16043 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
16044 * the only way to easily get the actual value because during the linking phase the interpolation
16045 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
16048 * function linkingFn(scope, elm, attrs, ctrl) {
16049 * // get the attribute value
16050 * console.log(attrs.ngModel);
16052 * // change the attribute
16053 * attrs.$set('ngModel', 'new value');
16055 * // observe changes to interpolated attribute
16056 * attrs.$observe('ngModel', function(value) {
16057 * console.log('ngModel has changed value to ' + value);
16064 * <div class="alert alert-warning">
16065 * **Note**: Typically directives are registered with `module.directive`. The example below is
16066 * to illustrate how `$compile` works.
16069 <example module="compileExample">
16070 <file name="index.html">
16072 angular.module('compileExample', [], function($compileProvider) {
16073 // configure new 'compile' directive by passing a directive
16074 // factory function. The factory function injects the '$compile'
16075 $compileProvider.directive('compile', function($compile) {
16076 // directive factory creates a link function
16077 return function(scope, element, attrs) {
16080 // watch the 'compile' expression for changes
16081 return scope.$eval(attrs.compile);
16084 // when the 'compile' expression changes
16085 // assign it into the current DOM
16086 element.html(value);
16088 // compile the new DOM and link it to the current
16090 // NOTE: we only compile .childNodes so that
16091 // we don't get into infinite loop compiling ourselves
16092 $compile(element.contents())(scope);
16098 .controller('GreeterController', ['$scope', function($scope) {
16099 $scope.name = 'Angular';
16100 $scope.html = 'Hello {{name}}';
16103 <div ng-controller="GreeterController">
16104 <input ng-model="name"> <br/>
16105 <textarea ng-model="html"></textarea> <br/>
16106 <div compile="html"></div>
16109 <file name="protractor.js" type="protractor">
16110 it('should auto compile', function() {
16111 var textarea = $('textarea');
16112 var output = $('div[compile]');
16113 // The initial state reads 'Hello Angular'.
16114 expect(output.getText()).toBe('Hello Angular');
16116 textarea.sendKeys('{{name}}!');
16117 expect(output.getText()).toBe('Angular!');
16124 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
16125 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
16127 * <div class="alert alert-danger">
16128 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
16129 * e.g. will not use the right outer scope. Please pass the transclude function as a
16130 * `parentBoundTranscludeFn` to the link function instead.
16133 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
16134 * root element(s), not their children)
16135 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
16136 * (a DOM element/tree) to a scope. Where:
16138 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
16139 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
16140 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
16141 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
16142 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
16144 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
16145 * * `scope` - is the current scope with which the linking function is working with.
16147 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
16148 * keys may be used to control linking behavior:
16150 * * `parentBoundTranscludeFn` - the transclude function made available to
16151 * directives; if given, it will be passed through to the link functions of
16152 * directives found in `element` during compilation.
16153 * * `transcludeControllers` - an object hash with keys that map controller names
16154 * to controller instances; if given, it will make the controllers
16155 * available to directives.
16156 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
16157 * the cloned elements; only needed for transcludes that are allowed to contain non html
16158 * elements (e.g. SVG elements). See also the directive.controller property.
16160 * Calling the linking function returns the element of the template. It is either the original
16161 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
16163 * After linking the view is not updated until after a call to $digest which typically is done by
16164 * Angular automatically.
16166 * If you need access to the bound view, there are two ways to do it:
16168 * - If you are not asking the linking function to clone the template, create the DOM element(s)
16169 * before you send them to the compiler and keep this reference around.
16171 * var element = $compile('<p>{{total}}</p>')(scope);
16174 * - if on the other hand, you need the element to be cloned, the view reference from the original
16175 * example would not point to the clone, but rather to the original template that was cloned. In
16176 * this case, you can access the clone via the cloneAttachFn:
16178 * var templateElement = angular.element('<p>{{total}}</p>'),
16181 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
16182 * //attach the clone to DOM document at the right place
16185 * //now we have reference to the cloned DOM via `clonedElement`
16189 * For information on how the compiler works, see the
16190 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
16193 var $compileMinErr = minErr('$compile');
16197 * @name $compileProvider
16201 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
16202 function $CompileProvider($provide, $$sanitizeUriProvider) {
16203 var hasDirectives = {},
16204 Suffix = 'Directive',
16205 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
16206 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
16207 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
16208 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
16210 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
16211 // The assumption is that future DOM event attribute names will begin with
16212 // 'on' and be composed of only English letters.
16213 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
16215 function parseIsolateBindings(scope, directiveName, isController) {
16216 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
16220 forEach(scope, function(definition, scopeName) {
16221 var match = definition.match(LOCAL_REGEXP);
16224 throw $compileMinErr('iscp',
16225 "Invalid {3} for directive '{0}'." +
16226 " Definition: {... {1}: '{2}' ...}",
16227 directiveName, scopeName, definition,
16228 (isController ? "controller bindings definition" :
16229 "isolate scope definition"));
16232 bindings[scopeName] = {
16234 collection: match[2] === '*',
16235 optional: match[3] === '?',
16236 attrName: match[4] || scopeName
16243 function parseDirectiveBindings(directive, directiveName) {
16245 isolateScope: null,
16246 bindToController: null
16248 if (isObject(directive.scope)) {
16249 if (directive.bindToController === true) {
16250 bindings.bindToController = parseIsolateBindings(directive.scope,
16251 directiveName, true);
16252 bindings.isolateScope = {};
16254 bindings.isolateScope = parseIsolateBindings(directive.scope,
16255 directiveName, false);
16258 if (isObject(directive.bindToController)) {
16259 bindings.bindToController =
16260 parseIsolateBindings(directive.bindToController, directiveName, true);
16262 if (isObject(bindings.bindToController)) {
16263 var controller = directive.controller;
16264 var controllerAs = directive.controllerAs;
16266 // There is no controller, there may or may not be a controllerAs property
16267 throw $compileMinErr('noctrl',
16268 "Cannot bind to controller without directive '{0}'s controller.",
16270 } else if (!identifierForController(controller, controllerAs)) {
16271 // There is a controller, but no identifier or controllerAs property
16272 throw $compileMinErr('noident',
16273 "Cannot bind to controller without identifier for directive '{0}'.",
16280 function assertValidDirectiveName(name) {
16281 var letter = name.charAt(0);
16282 if (!letter || letter !== lowercase(letter)) {
16283 throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
16285 if (name !== name.trim()) {
16286 throw $compileMinErr('baddir',
16287 "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
16294 * @name $compileProvider#directive
16298 * Register a new directive with the compiler.
16300 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
16301 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
16302 * names and the values are the factories.
16303 * @param {Function|Array} directiveFactory An injectable directive factory function. See
16304 * {@link guide/directive} for more info.
16305 * @returns {ng.$compileProvider} Self for chaining.
16307 this.directive = function registerDirective(name, directiveFactory) {
16308 assertNotHasOwnProperty(name, 'directive');
16309 if (isString(name)) {
16310 assertValidDirectiveName(name);
16311 assertArg(directiveFactory, 'directiveFactory');
16312 if (!hasDirectives.hasOwnProperty(name)) {
16313 hasDirectives[name] = [];
16314 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
16315 function($injector, $exceptionHandler) {
16316 var directives = [];
16317 forEach(hasDirectives[name], function(directiveFactory, index) {
16319 var directive = $injector.invoke(directiveFactory);
16320 if (isFunction(directive)) {
16321 directive = { compile: valueFn(directive) };
16322 } else if (!directive.compile && directive.link) {
16323 directive.compile = valueFn(directive.link);
16325 directive.priority = directive.priority || 0;
16326 directive.index = index;
16327 directive.name = directive.name || name;
16328 directive.require = directive.require || (directive.controller && directive.name);
16329 directive.restrict = directive.restrict || 'EA';
16330 var bindings = directive.$$bindings =
16331 parseDirectiveBindings(directive, directive.name);
16332 if (isObject(bindings.isolateScope)) {
16333 directive.$$isolateBindings = bindings.isolateScope;
16335 directive.$$moduleName = directiveFactory.$$moduleName;
16336 directives.push(directive);
16338 $exceptionHandler(e);
16344 hasDirectives[name].push(directiveFactory);
16346 forEach(name, reverseParams(registerDirective));
16354 * @name $compileProvider#aHrefSanitizationWhitelist
16358 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16359 * urls during a[href] sanitization.
16361 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
16363 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16364 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16365 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16366 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16368 * @param {RegExp=} regexp New regexp to whitelist urls with.
16369 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16370 * chaining otherwise.
16372 this.aHrefSanitizationWhitelist = function(regexp) {
16373 if (isDefined(regexp)) {
16374 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
16377 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
16384 * @name $compileProvider#imgSrcSanitizationWhitelist
16388 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16389 * urls during img[src] sanitization.
16391 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16393 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16394 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16395 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16396 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16398 * @param {RegExp=} regexp New regexp to whitelist urls with.
16399 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16400 * chaining otherwise.
16402 this.imgSrcSanitizationWhitelist = function(regexp) {
16403 if (isDefined(regexp)) {
16404 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
16407 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
16413 * @name $compileProvider#debugInfoEnabled
16415 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
16416 * current debugInfoEnabled state
16417 * @returns {*} current value if used as getter or itself (chaining) if used as setter
16422 * Call this method to enable/disable various debug runtime information in the compiler such as adding
16423 * binding information and a reference to the current scope on to DOM elements.
16424 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
16425 * * `ng-binding` CSS class
16426 * * `$binding` data property containing an array of the binding expressions
16428 * You may want to disable this in production for a significant performance boost. See
16429 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
16431 * The default value is true.
16433 var debugInfoEnabled = true;
16434 this.debugInfoEnabled = function(enabled) {
16435 if (isDefined(enabled)) {
16436 debugInfoEnabled = enabled;
16439 return debugInfoEnabled;
16443 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
16444 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
16445 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
16446 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
16448 var Attributes = function(element, attributesToCopy) {
16449 if (attributesToCopy) {
16450 var keys = Object.keys(attributesToCopy);
16453 for (i = 0, l = keys.length; i < l; i++) {
16455 this[key] = attributesToCopy[key];
16461 this.$$element = element;
16464 Attributes.prototype = {
16467 * @name $compile.directive.Attributes#$normalize
16471 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
16472 * `data-`) to its normalized, camelCase form.
16474 * Also there is special case for Moz prefix starting with upper case letter.
16476 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
16478 * @param {string} name Name to normalize
16480 $normalize: directiveNormalize,
16485 * @name $compile.directive.Attributes#$addClass
16489 * Adds the CSS class value specified by the classVal parameter to the element. If animations
16490 * are enabled then an animation will be triggered for the class addition.
16492 * @param {string} classVal The className value that will be added to the element
16494 $addClass: function(classVal) {
16495 if (classVal && classVal.length > 0) {
16496 $animate.addClass(this.$$element, classVal);
16502 * @name $compile.directive.Attributes#$removeClass
16506 * Removes the CSS class value specified by the classVal parameter from the element. If
16507 * animations are enabled then an animation will be triggered for the class removal.
16509 * @param {string} classVal The className value that will be removed from the element
16511 $removeClass: function(classVal) {
16512 if (classVal && classVal.length > 0) {
16513 $animate.removeClass(this.$$element, classVal);
16519 * @name $compile.directive.Attributes#$updateClass
16523 * Adds and removes the appropriate CSS class values to the element based on the difference
16524 * between the new and old CSS class values (specified as newClasses and oldClasses).
16526 * @param {string} newClasses The current CSS className value
16527 * @param {string} oldClasses The former CSS className value
16529 $updateClass: function(newClasses, oldClasses) {
16530 var toAdd = tokenDifference(newClasses, oldClasses);
16531 if (toAdd && toAdd.length) {
16532 $animate.addClass(this.$$element, toAdd);
16535 var toRemove = tokenDifference(oldClasses, newClasses);
16536 if (toRemove && toRemove.length) {
16537 $animate.removeClass(this.$$element, toRemove);
16542 * Set a normalized attribute on the element in a way such that all directives
16543 * can share the attribute. This function properly handles boolean attributes.
16544 * @param {string} key Normalized key. (ie ngAttribute)
16545 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
16546 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
16547 * Defaults to true.
16548 * @param {string=} attrName Optional none normalized name. Defaults to key.
16550 $set: function(key, value, writeAttr, attrName) {
16551 // TODO: decide whether or not to throw an error if "class"
16552 //is set through this function since it may cause $updateClass to
16555 var node = this.$$element[0],
16556 booleanKey = getBooleanAttrName(node, key),
16557 aliasedKey = getAliasedAttrName(key),
16562 this.$$element.prop(key, value);
16563 attrName = booleanKey;
16564 } else if (aliasedKey) {
16565 this[aliasedKey] = value;
16566 observer = aliasedKey;
16571 // translate normalized key to actual key
16573 this.$attr[key] = attrName;
16575 attrName = this.$attr[key];
16577 this.$attr[key] = attrName = snake_case(key, '-');
16581 nodeName = nodeName_(this.$$element);
16583 if ((nodeName === 'a' && key === 'href') ||
16584 (nodeName === 'img' && key === 'src')) {
16585 // sanitize a[href] and img[src] values
16586 this[key] = value = $$sanitizeUri(value, key === 'src');
16587 } else if (nodeName === 'img' && key === 'srcset') {
16588 // sanitize img[srcset] values
16591 // first check if there are spaces because it's not the same pattern
16592 var trimmedSrcset = trim(value);
16593 // ( 999x ,| 999w ,| ,|, )
16594 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
16595 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
16597 // split srcset into tuple of uri and descriptor except for the last item
16598 var rawUris = trimmedSrcset.split(pattern);
16601 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
16602 for (var i = 0; i < nbrUrisWith2parts; i++) {
16603 var innerIdx = i * 2;
16604 // sanitize the uri
16605 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
16606 // add the descriptor
16607 result += (" " + trim(rawUris[innerIdx + 1]));
16610 // split the last item into uri and descriptor
16611 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
16613 // sanitize the last uri
16614 result += $$sanitizeUri(trim(lastTuple[0]), true);
16616 // and add the last descriptor if any
16617 if (lastTuple.length === 2) {
16618 result += (" " + trim(lastTuple[1]));
16620 this[key] = value = result;
16623 if (writeAttr !== false) {
16624 if (value === null || isUndefined(value)) {
16625 this.$$element.removeAttr(attrName);
16627 this.$$element.attr(attrName, value);
16632 var $$observers = this.$$observers;
16633 $$observers && forEach($$observers[observer], function(fn) {
16637 $exceptionHandler(e);
16645 * @name $compile.directive.Attributes#$observe
16649 * Observes an interpolated attribute.
16651 * The observer function will be invoked once during the next `$digest` following
16652 * compilation. The observer is then invoked whenever the interpolated value
16655 * @param {string} key Normalized key. (ie ngAttribute) .
16656 * @param {function(interpolatedValue)} fn Function that will be called whenever
16657 the interpolated value of the attribute changes.
16658 * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
16659 * @returns {function()} Returns a deregistration function for this observer.
16661 $observe: function(key, fn) {
16663 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
16664 listeners = ($$observers[key] || ($$observers[key] = []));
16666 listeners.push(fn);
16667 $rootScope.$evalAsync(function() {
16668 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
16669 // no one registered attribute interpolation function, so lets call it manually
16674 return function() {
16675 arrayRemove(listeners, fn);
16681 function safeAddClass($element, className) {
16683 $element.addClass(className);
16685 // ignore, since it means that we are trying to set class on
16686 // SVG element, where class name is read-only.
16691 var startSymbol = $interpolate.startSymbol(),
16692 endSymbol = $interpolate.endSymbol(),
16693 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
16695 : function denormalizeTemplate(template) {
16696 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
16698 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
16699 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
16701 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
16702 var bindings = $element.data('$binding') || [];
16704 if (isArray(binding)) {
16705 bindings = bindings.concat(binding);
16707 bindings.push(binding);
16710 $element.data('$binding', bindings);
16713 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
16714 safeAddClass($element, 'ng-binding');
16717 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
16718 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
16719 $element.data(dataName, scope);
16722 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
16723 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
16728 //================================
16730 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
16731 previousCompileContext) {
16732 if (!($compileNodes instanceof jqLite)) {
16733 // jquery always rewraps, whereas we need to preserve the original selector so that we can
16735 $compileNodes = jqLite($compileNodes);
16737 // We can not compile top level text elements since text nodes can be merged and we will
16738 // not be able to attach scope data to them, so we will wrap them in <span>
16739 forEach($compileNodes, function(node, index) {
16740 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
16741 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
16744 var compositeLinkFn =
16745 compileNodes($compileNodes, transcludeFn, $compileNodes,
16746 maxPriority, ignoreDirective, previousCompileContext);
16747 compile.$$addScopeClass($compileNodes);
16748 var namespace = null;
16749 return function publicLinkFn(scope, cloneConnectFn, options) {
16750 assertArg(scope, 'scope');
16752 if (previousCompileContext && previousCompileContext.needsNewScope) {
16753 // A parent directive did a replace and a directive on this element asked
16754 // for transclusion, which caused us to lose a layer of element on which
16755 // we could hold the new transclusion scope, so we will create it manually
16757 scope = scope.$parent.$new();
16760 options = options || {};
16761 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
16762 transcludeControllers = options.transcludeControllers,
16763 futureParentElement = options.futureParentElement;
16765 // When `parentBoundTranscludeFn` is passed, it is a
16766 // `controllersBoundTransclude` function (it was previously passed
16767 // as `transclude` to directive.link) so we must unwrap it to get
16768 // its `boundTranscludeFn`
16769 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
16770 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
16774 namespace = detectNamespaceForChildElements(futureParentElement);
16777 if (namespace !== 'html') {
16778 // When using a directive with replace:true and templateUrl the $compileNodes
16779 // (or a child element inside of them)
16780 // might change, so we need to recreate the namespace adapted compileNodes
16781 // for call to the link function.
16782 // Note: This will already clone the nodes...
16783 $linkNode = jqLite(
16784 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
16786 } else if (cloneConnectFn) {
16787 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
16788 // and sometimes changes the structure of the DOM.
16789 $linkNode = JQLitePrototype.clone.call($compileNodes);
16791 $linkNode = $compileNodes;
16794 if (transcludeControllers) {
16795 for (var controllerName in transcludeControllers) {
16796 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
16800 compile.$$addScopeInfo($linkNode, scope);
16802 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
16803 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
16808 function detectNamespaceForChildElements(parentElement) {
16809 // TODO: Make this detect MathML as well...
16810 var node = parentElement && parentElement[0];
16814 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
16819 * Compile function matches each node in nodeList against the directives. Once all directives
16820 * for a particular node are collected their compile functions are executed. The compile
16821 * functions return values - the linking functions - are combined into a composite linking
16822 * function, which is the a linking function for the node.
16824 * @param {NodeList} nodeList an array of nodes or NodeList to compile
16825 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
16826 * scope argument is auto-generated to the new child of the transcluded parent scope.
16827 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
16828 * the rootElement must be set the jqLite collection of the compile root. This is
16829 * needed so that the jqLite collection items can be replaced with widgets.
16830 * @param {number=} maxPriority Max directive priority.
16831 * @returns {Function} A composite linking function of all of the matched directives or null.
16833 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
16834 previousCompileContext) {
16836 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
16838 for (var i = 0; i < nodeList.length; i++) {
16839 attrs = new Attributes();
16841 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
16842 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
16845 nodeLinkFn = (directives.length)
16846 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
16847 null, [], [], previousCompileContext)
16850 if (nodeLinkFn && nodeLinkFn.scope) {
16851 compile.$$addScopeClass(attrs.$$element);
16854 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
16855 !(childNodes = nodeList[i].childNodes) ||
16856 !childNodes.length)
16858 : compileNodes(childNodes,
16860 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
16861 && nodeLinkFn.transclude) : transcludeFn);
16863 if (nodeLinkFn || childLinkFn) {
16864 linkFns.push(i, nodeLinkFn, childLinkFn);
16865 linkFnFound = true;
16866 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
16869 //use the previous context only for the first element in the virtual group
16870 previousCompileContext = null;
16873 // return a linking function if we have found anything, null otherwise
16874 return linkFnFound ? compositeLinkFn : null;
16876 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
16877 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
16878 var stableNodeList;
16881 if (nodeLinkFnFound) {
16882 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
16883 // offsets don't get screwed up
16884 var nodeListLength = nodeList.length;
16885 stableNodeList = new Array(nodeListLength);
16887 // create a sparse array by only copying the elements which have a linkFn
16888 for (i = 0; i < linkFns.length; i+=3) {
16890 stableNodeList[idx] = nodeList[idx];
16893 stableNodeList = nodeList;
16896 for (i = 0, ii = linkFns.length; i < ii;) {
16897 node = stableNodeList[linkFns[i++]];
16898 nodeLinkFn = linkFns[i++];
16899 childLinkFn = linkFns[i++];
16902 if (nodeLinkFn.scope) {
16903 childScope = scope.$new();
16904 compile.$$addScopeInfo(jqLite(node), childScope);
16906 childScope = scope;
16909 if (nodeLinkFn.transcludeOnThisElement) {
16910 childBoundTranscludeFn = createBoundTranscludeFn(
16911 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
16913 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
16914 childBoundTranscludeFn = parentBoundTranscludeFn;
16916 } else if (!parentBoundTranscludeFn && transcludeFn) {
16917 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
16920 childBoundTranscludeFn = null;
16923 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
16925 } else if (childLinkFn) {
16926 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
16932 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
16934 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
16936 if (!transcludedScope) {
16937 transcludedScope = scope.$new(false, containingScope);
16938 transcludedScope.$$transcluded = true;
16941 return transcludeFn(transcludedScope, cloneFn, {
16942 parentBoundTranscludeFn: previousBoundTranscludeFn,
16943 transcludeControllers: controllers,
16944 futureParentElement: futureParentElement
16948 return boundTranscludeFn;
16952 * Looks for directives on the given node and adds them to the directive collection which is
16955 * @param node Node to search.
16956 * @param directives An array to which the directives are added to. This array is sorted before
16957 * the function returns.
16958 * @param attrs The shared attrs object which is used to populate the normalized attributes.
16959 * @param {number=} maxPriority Max directive priority.
16961 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
16962 var nodeType = node.nodeType,
16963 attrsMap = attrs.$attr,
16967 switch (nodeType) {
16968 case NODE_TYPE_ELEMENT: /* Element */
16969 // use the node name: <directive>
16970 addDirective(directives,
16971 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
16973 // iterate over the attributes
16974 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
16975 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
16976 var attrStartName = false;
16977 var attrEndName = false;
16981 value = trim(attr.value);
16983 // support ngAttr attribute binding
16984 ngAttrName = directiveNormalize(name);
16985 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
16986 name = name.replace(PREFIX_REGEXP, '')
16987 .substr(8).replace(/_(.)/g, function(match, letter) {
16988 return letter.toUpperCase();
16992 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
16993 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
16994 attrStartName = name;
16995 attrEndName = name.substr(0, name.length - 5) + 'end';
16996 name = name.substr(0, name.length - 6);
16999 nName = directiveNormalize(name.toLowerCase());
17000 attrsMap[nName] = name;
17001 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
17002 attrs[nName] = value;
17003 if (getBooleanAttrName(node, nName)) {
17004 attrs[nName] = true; // presence means true
17007 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
17008 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
17012 // use class as directive
17013 className = node.className;
17014 if (isObject(className)) {
17015 // Maybe SVGAnimatedString
17016 className = className.animVal;
17018 if (isString(className) && className !== '') {
17019 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
17020 nName = directiveNormalize(match[2]);
17021 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
17022 attrs[nName] = trim(match[3]);
17024 className = className.substr(match.index + match[0].length);
17028 case NODE_TYPE_TEXT: /* Text Node */
17030 // Workaround for #11781
17031 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
17032 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
17033 node.parentNode.removeChild(node.nextSibling);
17036 addTextInterpolateDirective(directives, node.nodeValue);
17038 case NODE_TYPE_COMMENT: /* Comment */
17040 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
17042 nName = directiveNormalize(match[1]);
17043 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
17044 attrs[nName] = trim(match[2]);
17048 // turns out that under some circumstances IE9 throws errors when one attempts to read
17049 // comment's node value.
17050 // Just ignore it and continue. (Can't seem to reproduce in test case.)
17055 directives.sort(byPriority);
17060 * Given a node with an directive-start it collects all of the siblings until it finds
17067 function groupScan(node, attrStart, attrEnd) {
17070 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
17073 throw $compileMinErr('uterdir',
17074 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
17075 attrStart, attrEnd);
17077 if (node.nodeType == NODE_TYPE_ELEMENT) {
17078 if (node.hasAttribute(attrStart)) depth++;
17079 if (node.hasAttribute(attrEnd)) depth--;
17082 node = node.nextSibling;
17083 } while (depth > 0);
17088 return jqLite(nodes);
17092 * Wrapper for linking function which converts normal linking function into a grouped
17093 * linking function.
17097 * @returns {Function}
17099 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
17100 return function(scope, element, attrs, controllers, transcludeFn) {
17101 element = groupScan(element[0], attrStart, attrEnd);
17102 return linkFn(scope, element, attrs, controllers, transcludeFn);
17107 * Once the directives have been collected, their compile functions are executed. This method
17108 * is responsible for inlining directive templates as well as terminating the application
17109 * of the directives if the terminal directive has been reached.
17111 * @param {Array} directives Array of collected directives to execute their compile function.
17112 * this needs to be pre-sorted by priority order.
17113 * @param {Node} compileNode The raw DOM node to apply the compile functions to
17114 * @param {Object} templateAttrs The shared attribute function
17115 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
17116 * scope argument is auto-generated to the new
17117 * child of the transcluded parent scope.
17118 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
17119 * argument has the root jqLite array so that we can replace nodes
17121 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
17122 * compiling the transclusion.
17123 * @param {Array.<Function>} preLinkFns
17124 * @param {Array.<Function>} postLinkFns
17125 * @param {Object} previousCompileContext Context used for previous compilation of the current
17127 * @returns {Function} linkFn
17129 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
17130 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
17131 previousCompileContext) {
17132 previousCompileContext = previousCompileContext || {};
17134 var terminalPriority = -Number.MAX_VALUE,
17135 newScopeDirective = previousCompileContext.newScopeDirective,
17136 controllerDirectives = previousCompileContext.controllerDirectives,
17137 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
17138 templateDirective = previousCompileContext.templateDirective,
17139 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
17140 hasTranscludeDirective = false,
17141 hasTemplate = false,
17142 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
17143 $compileNode = templateAttrs.$$element = jqLite(compileNode),
17147 replaceDirective = originalReplaceDirective,
17148 childTranscludeFn = transcludeFn,
17152 // executes all directives on the current element
17153 for (var i = 0, ii = directives.length; i < ii; i++) {
17154 directive = directives[i];
17155 var attrStart = directive.$$start;
17156 var attrEnd = directive.$$end;
17158 // collect multiblock sections
17160 $compileNode = groupScan(compileNode, attrStart, attrEnd);
17162 $template = undefined;
17164 if (terminalPriority > directive.priority) {
17165 break; // prevent further processing of directives
17168 if (directiveValue = directive.scope) {
17170 // skip the check for directives with async templates, we'll check the derived sync
17171 // directive when the template arrives
17172 if (!directive.templateUrl) {
17173 if (isObject(directiveValue)) {
17174 // This directive is trying to add an isolated scope.
17175 // Check that there is no scope of any kind already
17176 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
17177 directive, $compileNode);
17178 newIsolateScopeDirective = directive;
17180 // This directive is trying to add a child scope.
17181 // Check that there is no isolated scope already
17182 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
17187 newScopeDirective = newScopeDirective || directive;
17190 directiveName = directive.name;
17192 if (!directive.templateUrl && directive.controller) {
17193 directiveValue = directive.controller;
17194 controllerDirectives = controllerDirectives || createMap();
17195 assertNoDuplicate("'" + directiveName + "' controller",
17196 controllerDirectives[directiveName], directive, $compileNode);
17197 controllerDirectives[directiveName] = directive;
17200 if (directiveValue = directive.transclude) {
17201 hasTranscludeDirective = true;
17203 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
17204 // This option should only be used by directives that know how to safely handle element transclusion,
17205 // where the transcluded nodes are added or replaced after linking.
17206 if (!directive.$$tlb) {
17207 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
17208 nonTlbTranscludeDirective = directive;
17211 if (directiveValue == 'element') {
17212 hasElementTranscludeDirective = true;
17213 terminalPriority = directive.priority;
17214 $template = $compileNode;
17215 $compileNode = templateAttrs.$$element =
17216 jqLite(document.createComment(' ' + directiveName + ': ' +
17217 templateAttrs[directiveName] + ' '));
17218 compileNode = $compileNode[0];
17219 replaceWith(jqCollection, sliceArgs($template), compileNode);
17221 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
17222 replaceDirective && replaceDirective.name, {
17224 // - controllerDirectives - otherwise we'll create duplicates controllers
17225 // - newIsolateScopeDirective or templateDirective - combining templates with
17226 // element transclusion doesn't make sense.
17228 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
17229 // on the same element more than once.
17230 nonTlbTranscludeDirective: nonTlbTranscludeDirective
17233 $template = jqLite(jqLiteClone(compileNode)).contents();
17234 $compileNode.empty(); // clear contents
17235 childTranscludeFn = compile($template, transcludeFn, undefined,
17236 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
17240 if (directive.template) {
17241 hasTemplate = true;
17242 assertNoDuplicate('template', templateDirective, directive, $compileNode);
17243 templateDirective = directive;
17245 directiveValue = (isFunction(directive.template))
17246 ? directive.template($compileNode, templateAttrs)
17247 : directive.template;
17249 directiveValue = denormalizeTemplate(directiveValue);
17251 if (directive.replace) {
17252 replaceDirective = directive;
17253 if (jqLiteIsTextNode(directiveValue)) {
17256 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
17258 compileNode = $template[0];
17260 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
17261 throw $compileMinErr('tplrt',
17262 "Template for directive '{0}' must have exactly one root element. {1}",
17263 directiveName, '');
17266 replaceWith(jqCollection, $compileNode, compileNode);
17268 var newTemplateAttrs = {$attr: {}};
17270 // combine directives from the original node and from the template:
17271 // - take the array of directives for this element
17272 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
17273 // - collect directives from the template and sort them by priority
17274 // - combine directives as: processed + template + unprocessed
17275 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
17276 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
17278 if (newIsolateScopeDirective || newScopeDirective) {
17279 // The original directive caused the current element to be replaced but this element
17280 // also needs to have a new scope, so we need to tell the template directives
17281 // that they would need to get their scope from further up, if they require transclusion
17282 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
17284 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
17285 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
17287 ii = directives.length;
17289 $compileNode.html(directiveValue);
17293 if (directive.templateUrl) {
17294 hasTemplate = true;
17295 assertNoDuplicate('template', templateDirective, directive, $compileNode);
17296 templateDirective = directive;
17298 if (directive.replace) {
17299 replaceDirective = directive;
17302 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
17303 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
17304 controllerDirectives: controllerDirectives,
17305 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
17306 newIsolateScopeDirective: newIsolateScopeDirective,
17307 templateDirective: templateDirective,
17308 nonTlbTranscludeDirective: nonTlbTranscludeDirective
17310 ii = directives.length;
17311 } else if (directive.compile) {
17313 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
17314 if (isFunction(linkFn)) {
17315 addLinkFns(null, linkFn, attrStart, attrEnd);
17316 } else if (linkFn) {
17317 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
17320 $exceptionHandler(e, startingTag($compileNode));
17324 if (directive.terminal) {
17325 nodeLinkFn.terminal = true;
17326 terminalPriority = Math.max(terminalPriority, directive.priority);
17331 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
17332 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
17333 nodeLinkFn.templateOnThisElement = hasTemplate;
17334 nodeLinkFn.transclude = childTranscludeFn;
17336 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
17338 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
17341 ////////////////////
17343 function addLinkFns(pre, post, attrStart, attrEnd) {
17345 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
17346 pre.require = directive.require;
17347 pre.directiveName = directiveName;
17348 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
17349 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
17351 preLinkFns.push(pre);
17354 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
17355 post.require = directive.require;
17356 post.directiveName = directiveName;
17357 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
17358 post = cloneAndAnnotateFn(post, {isolateScope: true});
17360 postLinkFns.push(post);
17365 function getControllers(directiveName, require, $element, elementControllers) {
17368 if (isString(require)) {
17369 var match = require.match(REQUIRE_PREFIX_REGEXP);
17370 var name = require.substring(match[0].length);
17371 var inheritType = match[1] || match[3];
17372 var optional = match[2] === '?';
17374 //If only parents then start at the parent element
17375 if (inheritType === '^^') {
17376 $element = $element.parent();
17377 //Otherwise attempt getting the controller from elementControllers in case
17378 //the element is transcluded (and has no data) and to avoid .data if possible
17380 value = elementControllers && elementControllers[name];
17381 value = value && value.instance;
17385 var dataName = '$' + name + 'Controller';
17386 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
17389 if (!value && !optional) {
17390 throw $compileMinErr('ctreq',
17391 "Controller '{0}', required by directive '{1}', can't be found!",
17392 name, directiveName);
17394 } else if (isArray(require)) {
17396 for (var i = 0, ii = require.length; i < ii; i++) {
17397 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
17401 return value || null;
17404 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
17405 var elementControllers = createMap();
17406 for (var controllerKey in controllerDirectives) {
17407 var directive = controllerDirectives[controllerKey];
17409 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
17410 $element: $element,
17412 $transclude: transcludeFn
17415 var controller = directive.controller;
17416 if (controller == '@') {
17417 controller = attrs[directive.name];
17420 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
17422 // For directives with element transclusion the element is a comment,
17423 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
17424 // clean up (http://bugs.jquery.com/ticket/8335).
17425 // Instead, we save the controllers for the element in a local hash and attach to .data
17426 // later, once we have the actual element.
17427 elementControllers[directive.name] = controllerInstance;
17428 if (!hasElementTranscludeDirective) {
17429 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
17432 return elementControllers;
17435 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
17436 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
17437 attrs, removeScopeBindingWatches, removeControllerBindingWatches;
17439 if (compileNode === linkNode) {
17440 attrs = templateAttrs;
17441 $element = templateAttrs.$$element;
17443 $element = jqLite(linkNode);
17444 attrs = new Attributes($element, templateAttrs);
17447 controllerScope = scope;
17448 if (newIsolateScopeDirective) {
17449 isolateScope = scope.$new(true);
17450 } else if (newScopeDirective) {
17451 controllerScope = scope.$parent;
17454 if (boundTranscludeFn) {
17455 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
17456 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
17457 transcludeFn = controllersBoundTransclude;
17458 transcludeFn.$$boundTransclude = boundTranscludeFn;
17461 if (controllerDirectives) {
17462 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
17465 if (newIsolateScopeDirective) {
17466 // Initialize isolate scope bindings for new isolate scope directive.
17467 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
17468 templateDirective === newIsolateScopeDirective.$$originalDirective)));
17469 compile.$$addScopeClass($element, true);
17470 isolateScope.$$isolateBindings =
17471 newIsolateScopeDirective.$$isolateBindings;
17472 removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
17473 isolateScope.$$isolateBindings,
17474 newIsolateScopeDirective);
17475 if (removeScopeBindingWatches) {
17476 isolateScope.$on('$destroy', removeScopeBindingWatches);
17480 // Initialize bindToController bindings
17481 for (var name in elementControllers) {
17482 var controllerDirective = controllerDirectives[name];
17483 var controller = elementControllers[name];
17484 var bindings = controllerDirective.$$bindings.bindToController;
17486 if (controller.identifier && bindings) {
17487 removeControllerBindingWatches =
17488 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
17491 var controllerResult = controller();
17492 if (controllerResult !== controller.instance) {
17493 // If the controller constructor has a return value, overwrite the instance
17494 // from setupControllers
17495 controller.instance = controllerResult;
17496 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
17497 removeControllerBindingWatches && removeControllerBindingWatches();
17498 removeControllerBindingWatches =
17499 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
17504 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
17505 linkFn = preLinkFns[i];
17506 invokeLinkFn(linkFn,
17507 linkFn.isolateScope ? isolateScope : scope,
17510 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
17516 // We only pass the isolate scope, if the isolate directive has a template,
17517 // otherwise the child elements do not belong to the isolate directive.
17518 var scopeToChild = scope;
17519 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
17520 scopeToChild = isolateScope;
17522 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
17525 for (i = postLinkFns.length - 1; i >= 0; i--) {
17526 linkFn = postLinkFns[i];
17527 invokeLinkFn(linkFn,
17528 linkFn.isolateScope ? isolateScope : scope,
17531 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
17536 // This is the function that is injected as `$transclude`.
17537 // Note: all arguments are optional!
17538 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
17539 var transcludeControllers;
17541 // No scope passed in:
17542 if (!isScope(scope)) {
17543 futureParentElement = cloneAttachFn;
17544 cloneAttachFn = scope;
17548 if (hasElementTranscludeDirective) {
17549 transcludeControllers = elementControllers;
17551 if (!futureParentElement) {
17552 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
17554 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
17559 // Depending upon the context in which a directive finds itself it might need to have a new isolated
17560 // or child scope created. For instance:
17561 // * if the directive has been pulled into a template because another directive with a higher priority
17562 // asked for element transclusion
17563 // * if the directive itself asks for transclusion but it is at the root of a template and the original
17564 // element was replaced. See https://github.com/angular/angular.js/issues/12936
17565 function markDirectiveScope(directives, isolateScope, newScope) {
17566 for (var j = 0, jj = directives.length; j < jj; j++) {
17567 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
17572 * looks up the directive and decorates it with exception handling and proper parameters. We
17573 * call this the boundDirective.
17575 * @param {string} name name of the directive to look up.
17576 * @param {string} location The directive must be found in specific format.
17577 * String containing any of theses characters:
17579 * * `E`: element name
17583 * @returns {boolean} true if directive was added.
17585 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
17587 if (name === ignoreDirective) return null;
17589 if (hasDirectives.hasOwnProperty(name)) {
17590 for (var directive, directives = $injector.get(name + Suffix),
17591 i = 0, ii = directives.length; i < ii; i++) {
17593 directive = directives[i];
17594 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
17595 directive.restrict.indexOf(location) != -1) {
17596 if (startAttrName) {
17597 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
17599 tDirectives.push(directive);
17602 } catch (e) { $exceptionHandler(e); }
17610 * looks up the directive and returns true if it is a multi-element directive,
17611 * and therefore requires DOM nodes between -start and -end markers to be grouped
17614 * @param {string} name name of the directive to look up.
17615 * @returns true if directive was registered as multi-element.
17617 function directiveIsMultiElement(name) {
17618 if (hasDirectives.hasOwnProperty(name)) {
17619 for (var directive, directives = $injector.get(name + Suffix),
17620 i = 0, ii = directives.length; i < ii; i++) {
17621 directive = directives[i];
17622 if (directive.multiElement) {
17631 * When the element is replaced with HTML template then the new attributes
17632 * on the template need to be merged with the existing attributes in the DOM.
17633 * The desired effect is to have both of the attributes present.
17635 * @param {object} dst destination attributes (original DOM)
17636 * @param {object} src source attributes (from the directive template)
17638 function mergeTemplateAttributes(dst, src) {
17639 var srcAttr = src.$attr,
17640 dstAttr = dst.$attr,
17641 $element = dst.$$element;
17643 // reapply the old attributes to the new element
17644 forEach(dst, function(value, key) {
17645 if (key.charAt(0) != '$') {
17646 if (src[key] && src[key] !== value) {
17647 value += (key === 'style' ? ';' : ' ') + src[key];
17649 dst.$set(key, value, true, srcAttr[key]);
17653 // copy the new attributes on the old attrs object
17654 forEach(src, function(value, key) {
17655 if (key == 'class') {
17656 safeAddClass($element, value);
17657 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
17658 } else if (key == 'style') {
17659 $element.attr('style', $element.attr('style') + ';' + value);
17660 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
17661 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
17662 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
17663 // have an attribute like "has-own-property" or "data-has-own-property", etc.
17664 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
17666 dstAttr[key] = srcAttr[key];
17672 function compileTemplateUrl(directives, $compileNode, tAttrs,
17673 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
17674 var linkQueue = [],
17675 afterTemplateNodeLinkFn,
17676 afterTemplateChildLinkFn,
17677 beforeTemplateCompileNode = $compileNode[0],
17678 origAsyncDirective = directives.shift(),
17679 derivedSyncDirective = inherit(origAsyncDirective, {
17680 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
17682 templateUrl = (isFunction(origAsyncDirective.templateUrl))
17683 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
17684 : origAsyncDirective.templateUrl,
17685 templateNamespace = origAsyncDirective.templateNamespace;
17687 $compileNode.empty();
17689 $templateRequest(templateUrl)
17690 .then(function(content) {
17691 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
17693 content = denormalizeTemplate(content);
17695 if (origAsyncDirective.replace) {
17696 if (jqLiteIsTextNode(content)) {
17699 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
17701 compileNode = $template[0];
17703 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
17704 throw $compileMinErr('tplrt',
17705 "Template for directive '{0}' must have exactly one root element. {1}",
17706 origAsyncDirective.name, templateUrl);
17709 tempTemplateAttrs = {$attr: {}};
17710 replaceWith($rootElement, $compileNode, compileNode);
17711 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
17713 if (isObject(origAsyncDirective.scope)) {
17714 // the original directive that caused the template to be loaded async required
17715 // an isolate scope
17716 markDirectiveScope(templateDirectives, true);
17718 directives = templateDirectives.concat(directives);
17719 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
17721 compileNode = beforeTemplateCompileNode;
17722 $compileNode.html(content);
17725 directives.unshift(derivedSyncDirective);
17727 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
17728 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
17729 previousCompileContext);
17730 forEach($rootElement, function(node, i) {
17731 if (node == compileNode) {
17732 $rootElement[i] = $compileNode[0];
17735 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
17737 while (linkQueue.length) {
17738 var scope = linkQueue.shift(),
17739 beforeTemplateLinkNode = linkQueue.shift(),
17740 linkRootElement = linkQueue.shift(),
17741 boundTranscludeFn = linkQueue.shift(),
17742 linkNode = $compileNode[0];
17744 if (scope.$$destroyed) continue;
17746 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
17747 var oldClasses = beforeTemplateLinkNode.className;
17749 if (!(previousCompileContext.hasElementTranscludeDirective &&
17750 origAsyncDirective.replace)) {
17751 // it was cloned therefore we have to clone as well.
17752 linkNode = jqLiteClone(compileNode);
17754 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
17756 // Copy in CSS classes from original node
17757 safeAddClass(jqLite(linkNode), oldClasses);
17759 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
17760 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
17762 childBoundTranscludeFn = boundTranscludeFn;
17764 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
17765 childBoundTranscludeFn);
17770 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
17771 var childBoundTranscludeFn = boundTranscludeFn;
17772 if (scope.$$destroyed) return;
17774 linkQueue.push(scope,
17777 childBoundTranscludeFn);
17779 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
17780 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
17782 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
17789 * Sorting function for bound directives.
17791 function byPriority(a, b) {
17792 var diff = b.priority - a.priority;
17793 if (diff !== 0) return diff;
17794 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
17795 return a.index - b.index;
17798 function assertNoDuplicate(what, previousDirective, directive, element) {
17800 function wrapModuleNameIfDefined(moduleName) {
17801 return moduleName ?
17802 (' (module: ' + moduleName + ')') :
17806 if (previousDirective) {
17807 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
17808 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
17809 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
17814 function addTextInterpolateDirective(directives, text) {
17815 var interpolateFn = $interpolate(text, true);
17816 if (interpolateFn) {
17819 compile: function textInterpolateCompileFn(templateNode) {
17820 var templateNodeParent = templateNode.parent(),
17821 hasCompileParent = !!templateNodeParent.length;
17823 // When transcluding a template that has bindings in the root
17824 // we don't have a parent and thus need to add the class during linking fn.
17825 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
17827 return function textInterpolateLinkFn(scope, node) {
17828 var parent = node.parent();
17829 if (!hasCompileParent) compile.$$addBindingClass(parent);
17830 compile.$$addBindingInfo(parent, interpolateFn.expressions);
17831 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
17832 node[0].nodeValue = value;
17841 function wrapTemplate(type, template) {
17842 type = lowercase(type || 'html');
17846 var wrapper = document.createElement('div');
17847 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
17848 return wrapper.childNodes[0].childNodes;
17855 function getTrustedContext(node, attrNormalizedName) {
17856 if (attrNormalizedName == "srcdoc") {
17859 var tag = nodeName_(node);
17860 // maction[xlink:href] can source SVG. It's not limited to <maction>.
17861 if (attrNormalizedName == "xlinkHref" ||
17862 (tag == "form" && attrNormalizedName == "action") ||
17863 (tag != "img" && (attrNormalizedName == "src" ||
17864 attrNormalizedName == "ngSrc"))) {
17865 return $sce.RESOURCE_URL;
17870 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
17871 var trustedContext = getTrustedContext(node, name);
17872 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
17874 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
17876 // no interpolation found -> ignore
17877 if (!interpolateFn) return;
17880 if (name === "multiple" && nodeName_(node) === "select") {
17881 throw $compileMinErr("selmulti",
17882 "Binding to the 'multiple' attribute is not supported. Element: {0}",
17883 startingTag(node));
17888 compile: function() {
17890 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
17891 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
17893 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
17894 throw $compileMinErr('nodomevents',
17895 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
17896 "ng- versions (such as ng-click instead of onclick) instead.");
17899 // If the attribute has changed since last $interpolate()ed
17900 var newValue = attr[name];
17901 if (newValue !== value) {
17902 // we need to interpolate again since the attribute value has been updated
17903 // (e.g. by another directive's compile function)
17904 // ensure unset/empty values make interpolateFn falsy
17905 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
17909 // if attribute was updated so that there is no interpolation going on we don't want to
17910 // register any observers
17911 if (!interpolateFn) return;
17913 // initialize attr object so that it's ready in case we need the value for isolate
17914 // scope initialization, otherwise the value would not be available from isolate
17915 // directive's linking fn during linking phase
17916 attr[name] = interpolateFn(scope);
17918 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
17919 (attr.$$observers && attr.$$observers[name].$$scope || scope).
17920 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
17921 //special case for class attribute addition + removal
17922 //so that class changes can tap into the animation
17923 //hooks provided by the $animate service. Be sure to
17924 //skip animations when the first digest occurs (when
17925 //both the new and the old values are the same) since
17926 //the CSS classes are the non-interpolated values
17927 if (name === 'class' && newValue != oldValue) {
17928 attr.$updateClass(newValue, oldValue);
17930 attr.$set(name, newValue);
17941 * This is a special jqLite.replaceWith, which can replace items which
17942 * have no parents, provided that the containing jqLite collection is provided.
17944 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
17945 * in the root of the tree.
17946 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
17947 * the shell, but replace its DOM node reference.
17948 * @param {Node} newNode The new DOM node.
17950 function replaceWith($rootElement, elementsToRemove, newNode) {
17951 var firstElementToRemove = elementsToRemove[0],
17952 removeCount = elementsToRemove.length,
17953 parent = firstElementToRemove.parentNode,
17956 if ($rootElement) {
17957 for (i = 0, ii = $rootElement.length; i < ii; i++) {
17958 if ($rootElement[i] == firstElementToRemove) {
17959 $rootElement[i++] = newNode;
17960 for (var j = i, j2 = j + removeCount - 1,
17961 jj = $rootElement.length;
17962 j < jj; j++, j2++) {
17964 $rootElement[j] = $rootElement[j2];
17966 delete $rootElement[j];
17969 $rootElement.length -= removeCount - 1;
17971 // If the replaced element is also the jQuery .context then replace it
17972 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
17973 // http://api.jquery.com/context/
17974 if ($rootElement.context === firstElementToRemove) {
17975 $rootElement.context = newNode;
17983 parent.replaceChild(newNode, firstElementToRemove);
17986 // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
17987 var fragment = document.createDocumentFragment();
17988 fragment.appendChild(firstElementToRemove);
17990 if (jqLite.hasData(firstElementToRemove)) {
17991 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
17992 // data here because there's no public interface in jQuery to do that and copying over
17993 // event listeners (which is the main use of private data) wouldn't work anyway.
17994 jqLite.data(newNode, jqLite.data(firstElementToRemove));
17996 // Remove data of the replaced element. We cannot just call .remove()
17997 // on the element it since that would deallocate scope that is needed
17998 // for the new node. Instead, remove the data "manually".
18000 delete jqLite.cache[firstElementToRemove[jqLite.expando]];
18002 // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
18003 // the replaced element. The cleanData version monkey-patched by Angular would cause
18004 // the scope to be trashed and we do need the very same scope to work with the new
18005 // element. However, we cannot just cache the non-patched version and use it here as
18006 // that would break if another library patches the method after Angular does (one
18007 // example is jQuery UI). Instead, set a flag indicating scope destroying should be
18008 // skipped this one time.
18009 skipDestroyOnNextJQueryCleanData = true;
18010 jQuery.cleanData([firstElementToRemove]);
18014 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
18015 var element = elementsToRemove[k];
18016 jqLite(element).remove(); // must do this way to clean up expando
18017 fragment.appendChild(element);
18018 delete elementsToRemove[k];
18021 elementsToRemove[0] = newNode;
18022 elementsToRemove.length = 1;
18026 function cloneAndAnnotateFn(fn, annotation) {
18027 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
18031 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
18033 linkFn(scope, $element, attrs, controllers, transcludeFn);
18035 $exceptionHandler(e, startingTag($element));
18040 // Set up $watches for isolate scope and controller bindings. This process
18041 // only occurs for isolate scopes and new scopes with controllerAs.
18042 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
18043 var removeWatchCollection = [];
18044 forEach(bindings, function(definition, scopeName) {
18045 var attrName = definition.attrName,
18046 optional = definition.optional,
18047 mode = definition.mode, // @, =, or &
18049 parentGet, parentSet, compare;
18054 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
18055 destination[scopeName] = attrs[attrName] = void 0;
18057 attrs.$observe(attrName, function(value) {
18058 if (isString(value)) {
18059 destination[scopeName] = value;
18062 attrs.$$observers[attrName].$$scope = scope;
18063 if (isString(attrs[attrName])) {
18064 // If the attribute has been provided then we trigger an interpolation to ensure
18065 // the value is there for use in the link fn
18066 destination[scopeName] = $interpolate(attrs[attrName])(scope);
18071 if (!hasOwnProperty.call(attrs, attrName)) {
18072 if (optional) break;
18073 attrs[attrName] = void 0;
18075 if (optional && !attrs[attrName]) break;
18077 parentGet = $parse(attrs[attrName]);
18078 if (parentGet.literal) {
18081 compare = function(a, b) { return a === b || (a !== a && b !== b); };
18083 parentSet = parentGet.assign || function() {
18084 // reset the change, or we will throw this exception on every $digest
18085 lastValue = destination[scopeName] = parentGet(scope);
18086 throw $compileMinErr('nonassign',
18087 "Expression '{0}' used with directive '{1}' is non-assignable!",
18088 attrs[attrName], directive.name);
18090 lastValue = destination[scopeName] = parentGet(scope);
18091 var parentValueWatch = function parentValueWatch(parentValue) {
18092 if (!compare(parentValue, destination[scopeName])) {
18093 // we are out of sync and need to copy
18094 if (!compare(parentValue, lastValue)) {
18095 // parent changed and it has precedence
18096 destination[scopeName] = parentValue;
18098 // if the parent can be assigned then do so
18099 parentSet(scope, parentValue = destination[scopeName]);
18102 return lastValue = parentValue;
18104 parentValueWatch.$stateful = true;
18106 if (definition.collection) {
18107 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
18109 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
18111 removeWatchCollection.push(removeWatch);
18115 // Don't assign Object.prototype method to scope
18116 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
18118 // Don't assign noop to destination if expression is not valid
18119 if (parentGet === noop && optional) break;
18121 destination[scopeName] = function(locals) {
18122 return parentGet(scope, locals);
18128 return removeWatchCollection.length && function removeWatches() {
18129 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
18130 removeWatchCollection[i]();
18137 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
18139 * Converts all accepted directives format into proper directive name.
18140 * @param name Name to normalize
18142 function directiveNormalize(name) {
18143 return camelCase(name.replace(PREFIX_REGEXP, ''));
18148 * @name $compile.directive.Attributes
18151 * A shared object between directive compile / linking functions which contains normalized DOM
18152 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
18153 * needed since all of these are treated as equivalent in Angular:
18156 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
18162 * @name $compile.directive.Attributes#$attr
18165 * A map of DOM element attribute names to the normalized name. This is
18166 * needed to do reverse lookup from normalized name back to actual name.
18172 * @name $compile.directive.Attributes#$set
18176 * Set DOM element attribute value.
18179 * @param {string} name Normalized element attribute name of the property to modify. The name is
18180 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
18181 * property to the original name.
18182 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
18188 * Closure compiler type information
18191 function nodesetLinkingFn(
18192 /* angular.Scope */ scope,
18193 /* NodeList */ nodeList,
18194 /* Element */ rootElement,
18195 /* function(Function) */ boundTranscludeFn
18198 function directiveLinkingFn(
18199 /* nodesetLinkingFn */ nodesetLinkingFn,
18200 /* angular.Scope */ scope,
18202 /* Element */ rootElement,
18203 /* function(Function) */ boundTranscludeFn
18206 function tokenDifference(str1, str2) {
18208 tokens1 = str1.split(/\s+/),
18209 tokens2 = str2.split(/\s+/);
18212 for (var i = 0; i < tokens1.length; i++) {
18213 var token = tokens1[i];
18214 for (var j = 0; j < tokens2.length; j++) {
18215 if (token == tokens2[j]) continue outer;
18217 values += (values.length > 0 ? ' ' : '') + token;
18222 function removeComments(jqNodes) {
18223 jqNodes = jqLite(jqNodes);
18224 var i = jqNodes.length;
18231 var node = jqNodes[i];
18232 if (node.nodeType === NODE_TYPE_COMMENT) {
18233 splice.call(jqNodes, i, 1);
18239 var $controllerMinErr = minErr('$controller');
18242 var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
18243 function identifierForController(controller, ident) {
18244 if (ident && isString(ident)) return ident;
18245 if (isString(controller)) {
18246 var match = CNTRL_REG.exec(controller);
18247 if (match) return match[3];
18254 * @name $controllerProvider
18256 * The {@link ng.$controller $controller service} is used by Angular to create new
18259 * This provider allows controller registration via the
18260 * {@link ng.$controllerProvider#register register} method.
18262 function $ControllerProvider() {
18263 var controllers = {},
18268 * @name $controllerProvider#register
18269 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
18270 * the names and the values are the constructors.
18271 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
18272 * annotations in the array notation).
18274 this.register = function(name, constructor) {
18275 assertNotHasOwnProperty(name, 'controller');
18276 if (isObject(name)) {
18277 extend(controllers, name);
18279 controllers[name] = constructor;
18285 * @name $controllerProvider#allowGlobals
18286 * @description If called, allows `$controller` to find controller constructors on `window`
18288 this.allowGlobals = function() {
18293 this.$get = ['$injector', '$window', function($injector, $window) {
18297 * @name $controller
18298 * @requires $injector
18300 * @param {Function|string} constructor If called with a function then it's considered to be the
18301 * controller constructor function. Otherwise it's considered to be a string which is used
18302 * to retrieve the controller constructor using the following steps:
18304 * * check if a controller with given name is registered via `$controllerProvider`
18305 * * check if evaluating the string on the current scope returns a constructor
18306 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
18307 * `window` object (not recommended)
18309 * The string can use the `controller as property` syntax, where the controller instance is published
18310 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
18311 * to work correctly.
18313 * @param {Object} locals Injection locals for Controller.
18314 * @return {Object} Instance of given controller.
18317 * `$controller` service is responsible for instantiating controllers.
18319 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
18320 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
18322 return function(expression, locals, later, ident) {
18324 // param `later` --- indicates that the controller's constructor is invoked at a later time.
18325 // If true, $controller will allocate the object with the correct
18326 // prototype chain, but will not invoke the controller until a returned
18327 // callback is invoked.
18328 // param `ident` --- An optional label which overrides the label parsed from the controller
18329 // expression, if any.
18330 var instance, match, constructor, identifier;
18331 later = later === true;
18332 if (ident && isString(ident)) {
18333 identifier = ident;
18336 if (isString(expression)) {
18337 match = expression.match(CNTRL_REG);
18339 throw $controllerMinErr('ctrlfmt',
18340 "Badly formed controller string '{0}'. " +
18341 "Must match `__name__ as __id__` or `__name__`.", expression);
18343 constructor = match[1],
18344 identifier = identifier || match[3];
18345 expression = controllers.hasOwnProperty(constructor)
18346 ? controllers[constructor]
18347 : getter(locals.$scope, constructor, true) ||
18348 (globals ? getter($window, constructor, true) : undefined);
18350 assertArgFn(expression, constructor, true);
18354 // Instantiate controller later:
18355 // This machinery is used to create an instance of the object before calling the
18356 // controller's constructor itself.
18358 // This allows properties to be added to the controller before the constructor is
18359 // invoked. Primarily, this is used for isolate scope bindings in $compile.
18361 // This feature is not intended for use by applications, and is thus not documented
18363 // Object creation: http://jsperf.com/create-constructor/2
18364 var controllerPrototype = (isArray(expression) ?
18365 expression[expression.length - 1] : expression).prototype;
18366 instance = Object.create(controllerPrototype || null);
18369 addIdentifier(locals, identifier, instance, constructor || expression.name);
18373 return instantiate = extend(function() {
18374 var result = $injector.invoke(expression, instance, locals, constructor);
18375 if (result !== instance && (isObject(result) || isFunction(result))) {
18378 // If result changed, re-assign controllerAs value to scope.
18379 addIdentifier(locals, identifier, instance, constructor || expression.name);
18384 instance: instance,
18385 identifier: identifier
18389 instance = $injector.instantiate(expression, locals, constructor);
18392 addIdentifier(locals, identifier, instance, constructor || expression.name);
18398 function addIdentifier(locals, identifier, instance, name) {
18399 if (!(locals && isObject(locals.$scope))) {
18400 throw minErr('$controller')('noscp',
18401 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
18405 locals.$scope[identifier] = instance;
18413 * @requires $window
18416 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
18419 <example module="documentExample">
18420 <file name="index.html">
18421 <div ng-controller="ExampleController">
18422 <p>$document title: <b ng-bind="title"></b></p>
18423 <p>window.document title: <b ng-bind="windowTitle"></b></p>
18426 <file name="script.js">
18427 angular.module('documentExample', [])
18428 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
18429 $scope.title = $document[0].title;
18430 $scope.windowTitle = angular.element(window.document)[0].title;
18435 function $DocumentProvider() {
18436 this.$get = ['$window', function(window) {
18437 return jqLite(window.document);
18443 * @name $exceptionHandler
18444 * @requires ng.$log
18447 * Any uncaught exception in angular expressions is delegated to this service.
18448 * The default implementation simply delegates to `$log.error` which logs it into
18449 * the browser console.
18451 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
18452 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
18457 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
18458 * return function(exception, cause) {
18459 * exception.message += ' (caused by "' + cause + '")';
18465 * This example will override the normal action of `$exceptionHandler`, to make angular
18466 * exceptions fail hard when they happen, instead of just logging to the console.
18469 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
18470 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
18471 * (unless executed during a digest).
18473 * If you wish, you can manually delegate exceptions, e.g.
18474 * `try { ... } catch(e) { $exceptionHandler(e); }`
18476 * @param {Error} exception Exception associated with the error.
18477 * @param {string=} cause optional information about the context in which
18478 * the error was thrown.
18481 function $ExceptionHandlerProvider() {
18482 this.$get = ['$log', function($log) {
18483 return function(exception, cause) {
18484 $log.error.apply($log, arguments);
18489 var $$ForceReflowProvider = function() {
18490 this.$get = ['$document', function($document) {
18491 return function(domNode) {
18492 //the line below will force the browser to perform a repaint so
18493 //that all the animated elements within the animation frame will
18494 //be properly updated and drawn on screen. This is required to
18495 //ensure that the preparation animation is properly flushed so that
18496 //the active state picks up from there. DO NOT REMOVE THIS LINE.
18497 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
18498 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
18499 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
18501 if (!domNode.nodeType && domNode instanceof jqLite) {
18502 domNode = domNode[0];
18505 domNode = $document[0].body;
18507 return domNode.offsetWidth + 1;
18512 var APPLICATION_JSON = 'application/json';
18513 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
18514 var JSON_START = /^\[|^\{(?!\{)/;
18519 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
18520 var $httpMinErr = minErr('$http');
18521 var $httpMinErrLegacyFn = function(method) {
18522 return function() {
18523 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
18527 function serializeValue(v) {
18529 return isDate(v) ? v.toISOString() : toJson(v);
18535 function $HttpParamSerializerProvider() {
18538 * @name $httpParamSerializer
18541 * Default {@link $http `$http`} params serializer that converts objects to strings
18542 * according to the following rules:
18544 * * `{'foo': 'bar'}` results in `foo=bar`
18545 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
18546 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
18547 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
18549 * Note that serializer will sort the request parameters alphabetically.
18552 this.$get = function() {
18553 return function ngParamSerializer(params) {
18554 if (!params) return '';
18556 forEachSorted(params, function(value, key) {
18557 if (value === null || isUndefined(value)) return;
18558 if (isArray(value)) {
18559 forEach(value, function(v, k) {
18560 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
18563 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
18567 return parts.join('&');
18572 function $HttpParamSerializerJQLikeProvider() {
18575 * @name $httpParamSerializerJQLike
18578 * Alternative {@link $http `$http`} params serializer that follows
18579 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
18580 * The serializer will also sort the params alphabetically.
18582 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
18588 * params: myParams,
18589 * paramSerializer: '$httpParamSerializerJQLike'
18593 * It is also possible to set it as the default `paramSerializer` in the
18594 * {@link $httpProvider#defaults `$httpProvider`}.
18596 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
18597 * form data for submission:
18600 * .controller(function($http, $httpParamSerializerJQLike) {
18606 * data: $httpParamSerializerJQLike(myData),
18608 * 'Content-Type': 'application/x-www-form-urlencoded'
18616 this.$get = function() {
18617 return function jQueryLikeParamSerializer(params) {
18618 if (!params) return '';
18620 serialize(params, '', true);
18621 return parts.join('&');
18623 function serialize(toSerialize, prefix, topLevel) {
18624 if (toSerialize === null || isUndefined(toSerialize)) return;
18625 if (isArray(toSerialize)) {
18626 forEach(toSerialize, function(value, index) {
18627 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
18629 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
18630 forEachSorted(toSerialize, function(value, key) {
18631 serialize(value, prefix +
18632 (topLevel ? '' : '[') +
18634 (topLevel ? '' : ']'));
18637 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
18644 function defaultHttpResponseTransform(data, headers) {
18645 if (isString(data)) {
18646 // Strip json vulnerability protection prefix and trim whitespace
18647 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
18650 var contentType = headers('Content-Type');
18651 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
18652 data = fromJson(tempData);
18660 function isJsonLike(str) {
18661 var jsonStart = str.match(JSON_START);
18662 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
18666 * Parse headers into key value object
18668 * @param {string} headers Raw headers as a string
18669 * @returns {Object} Parsed headers as key value object
18671 function parseHeaders(headers) {
18672 var parsed = createMap(), i;
18674 function fillInParsed(key, val) {
18676 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
18680 if (isString(headers)) {
18681 forEach(headers.split('\n'), function(line) {
18682 i = line.indexOf(':');
18683 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
18685 } else if (isObject(headers)) {
18686 forEach(headers, function(headerVal, headerKey) {
18687 fillInParsed(lowercase(headerKey), trim(headerVal));
18696 * Returns a function that provides access to parsed headers.
18698 * Headers are lazy parsed when first requested.
18699 * @see parseHeaders
18701 * @param {(string|Object)} headers Headers to provide access to.
18702 * @returns {function(string=)} Returns a getter function which if called with:
18704 * - if called with single an argument returns a single header value or null
18705 * - if called with no arguments returns an object containing all headers.
18707 function headersGetter(headers) {
18710 return function(name) {
18711 if (!headersObj) headersObj = parseHeaders(headers);
18714 var value = headersObj[lowercase(name)];
18715 if (value === void 0) {
18727 * Chain all given functions
18729 * This function is used for both request and response transforming
18731 * @param {*} data Data to transform.
18732 * @param {function(string=)} headers HTTP headers getter fn.
18733 * @param {number} status HTTP status code of the response.
18734 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
18735 * @returns {*} Transformed data.
18737 function transformData(data, headers, status, fns) {
18738 if (isFunction(fns)) {
18739 return fns(data, headers, status);
18742 forEach(fns, function(fn) {
18743 data = fn(data, headers, status);
18750 function isSuccess(status) {
18751 return 200 <= status && status < 300;
18757 * @name $httpProvider
18759 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
18761 function $HttpProvider() {
18764 * @name $httpProvider#defaults
18767 * Object containing default values for all {@link ng.$http $http} requests.
18769 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
18770 * that will provide the cache for all requests who set their `cache` property to `true`.
18771 * If you set the `defaults.cache = false` then only requests that specify their own custom
18772 * cache object will be cached. See {@link $http#caching $http Caching} for more information.
18774 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
18775 * Defaults value is `'XSRF-TOKEN'`.
18777 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
18778 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
18780 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
18781 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
18782 * setting default headers.
18783 * - **`defaults.headers.common`**
18784 * - **`defaults.headers.post`**
18785 * - **`defaults.headers.put`**
18786 * - **`defaults.headers.patch`**
18789 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
18790 * used to the prepare string representation of request parameters (specified as an object).
18791 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
18792 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
18795 var defaults = this.defaults = {
18796 // transform incoming response data
18797 transformResponse: [defaultHttpResponseTransform],
18799 // transform outgoing request data
18800 transformRequest: [function(d) {
18801 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
18807 'Accept': 'application/json, text/plain, */*'
18809 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
18810 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
18811 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
18814 xsrfCookieName: 'XSRF-TOKEN',
18815 xsrfHeaderName: 'X-XSRF-TOKEN',
18817 paramSerializer: '$httpParamSerializer'
18820 var useApplyAsync = false;
18823 * @name $httpProvider#useApplyAsync
18826 * Configure $http service to combine processing of multiple http responses received at around
18827 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
18828 * significant performance improvement for bigger applications that make many HTTP requests
18829 * concurrently (common during application bootstrap).
18831 * Defaults to false. If no value is specified, returns the current configured value.
18833 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
18834 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
18835 * to load and share the same digest cycle.
18837 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
18838 * otherwise, returns the current configured value.
18840 this.useApplyAsync = function(value) {
18841 if (isDefined(value)) {
18842 useApplyAsync = !!value;
18845 return useApplyAsync;
18848 var useLegacyPromise = true;
18851 * @name $httpProvider#useLegacyPromiseExtensions
18854 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
18855 * This should be used to make sure that applications work without these methods.
18857 * Defaults to true. If no value is specified, returns the current configured value.
18859 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
18861 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
18862 * otherwise, returns the current configured value.
18864 this.useLegacyPromiseExtensions = function(value) {
18865 if (isDefined(value)) {
18866 useLegacyPromise = !!value;
18869 return useLegacyPromise;
18874 * @name $httpProvider#interceptors
18877 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
18878 * pre-processing of request or postprocessing of responses.
18880 * These service factories are ordered by request, i.e. they are applied in the same order as the
18881 * array, on request, but reverse order, on response.
18883 * {@link ng.$http#interceptors Interceptors detailed info}
18885 var interceptorFactories = this.interceptors = [];
18887 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
18888 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
18890 var defaultCache = $cacheFactory('$http');
18893 * Make sure that default param serializer is exposed as a function
18895 defaults.paramSerializer = isString(defaults.paramSerializer) ?
18896 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
18899 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
18900 * The reversal is needed so that we can build up the interception chain around the
18903 var reversedInterceptors = [];
18905 forEach(interceptorFactories, function(interceptorFactory) {
18906 reversedInterceptors.unshift(isString(interceptorFactory)
18907 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
18914 * @requires ng.$httpBackend
18915 * @requires $cacheFactory
18916 * @requires $rootScope
18918 * @requires $injector
18921 * The `$http` service is a core Angular service that facilitates communication with the remote
18922 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
18923 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
18925 * For unit testing applications that use `$http` service, see
18926 * {@link ngMock.$httpBackend $httpBackend mock}.
18928 * For a higher level of abstraction, please check out the {@link ngResource.$resource
18929 * $resource} service.
18931 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
18932 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
18933 * it is important to familiarize yourself with these APIs and the guarantees they provide.
18937 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
18938 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
18941 * // Simple GET request example:
18945 * }).then(function successCallback(response) {
18946 * // this callback will be called asynchronously
18947 * // when the response is available
18948 * }, function errorCallback(response) {
18949 * // called asynchronously if an error occurs
18950 * // or server returns response with an error status.
18954 * The response object has these properties:
18956 * - **data** – `{string|Object}` – The response body transformed with the transform
18958 * - **status** – `{number}` – HTTP status code of the response.
18959 * - **headers** – `{function([headerName])}` – Header getter function.
18960 * - **config** – `{Object}` – The configuration object that was used to generate the request.
18961 * - **statusText** – `{string}` – HTTP status text of the response.
18963 * A response status code between 200 and 299 is considered a success status and
18964 * will result in the success callback being called. Note that if the response is a redirect,
18965 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
18966 * called for such responses.
18969 * ## Shortcut methods
18971 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
18972 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
18976 * $http.get('/someUrl', config).then(successCallback, errorCallback);
18977 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
18980 * Complete list of shortcut methods:
18982 * - {@link ng.$http#get $http.get}
18983 * - {@link ng.$http#head $http.head}
18984 * - {@link ng.$http#post $http.post}
18985 * - {@link ng.$http#put $http.put}
18986 * - {@link ng.$http#delete $http.delete}
18987 * - {@link ng.$http#jsonp $http.jsonp}
18988 * - {@link ng.$http#patch $http.patch}
18991 * ## Writing Unit Tests that use $http
18992 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
18993 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
18994 * request using trained responses.
18997 * $httpBackend.expectGET(...);
18999 * $httpBackend.flush();
19002 * ## Deprecation Notice
19003 * <div class="alert alert-danger">
19004 * The `$http` legacy promise methods `success` and `error` have been deprecated.
19005 * Use the standard `then` method instead.
19006 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
19007 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
19010 * ## Setting HTTP Headers
19012 * The $http service will automatically add certain HTTP headers to all requests. These defaults
19013 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
19014 * object, which currently contains this default configuration:
19016 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
19017 * - `Accept: application/json, text/plain, * / *`
19018 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
19019 * - `Content-Type: application/json`
19020 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
19021 * - `Content-Type: application/json`
19023 * To add or overwrite these defaults, simply add or remove a property from these configuration
19024 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
19025 * with the lowercased HTTP method name as the key, e.g.
19026 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
19028 * The defaults can also be set at runtime via the `$http.defaults` object in the same
19029 * fashion. For example:
19032 * module.run(function($http) {
19033 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
19037 * In addition, you can supply a `headers` property in the config object passed when
19038 * calling `$http(config)`, which overrides the defaults without changing them globally.
19040 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
19041 * Use the `headers` property, setting the desired header to `undefined`. For example:
19046 * url: 'http://example.com',
19048 * 'Content-Type': undefined
19050 * data: { test: 'test' }
19053 * $http(req).then(function(){...}, function(){...});
19056 * ## Transforming Requests and Responses
19058 * Both requests and responses can be transformed using transformation functions: `transformRequest`
19059 * and `transformResponse`. These properties can be a single function that returns
19060 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
19061 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
19063 * ### Default Transformations
19065 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
19066 * `defaults.transformResponse` properties. If a request does not provide its own transformations
19067 * then these will be applied.
19069 * You can augment or replace the default transformations by modifying these properties by adding to or
19070 * replacing the array.
19072 * Angular provides the following default transformations:
19074 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
19076 * - If the `data` property of the request configuration object contains an object, serialize it
19077 * into JSON format.
19079 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
19081 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
19082 * - If JSON response is detected, deserialize it using a JSON parser.
19085 * ### Overriding the Default Transformations Per Request
19087 * If you wish override the request/response transformations only for a single request then provide
19088 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
19091 * Note that if you provide these properties on the config object the default transformations will be
19092 * overwritten. If you wish to augment the default transformations then you must include them in your
19093 * local transformation array.
19095 * The following code demonstrates adding a new response transformation to be run after the default response
19096 * transformations have been run.
19099 * function appendTransform(defaults, transform) {
19101 * // We can't guarantee that the default transformation is an array
19102 * defaults = angular.isArray(defaults) ? defaults : [defaults];
19104 * // Append the new transformation to the defaults
19105 * return defaults.concat(transform);
19111 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
19112 * return doTransform(value);
19120 * To enable caching, set the request configuration `cache` property to `true` (to use default
19121 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
19122 * When the cache is enabled, `$http` stores the response from the server in the specified
19123 * cache. The next time the same request is made, the response is served from the cache without
19124 * sending a request to the server.
19126 * Note that even if the response is served from cache, delivery of the data is asynchronous in
19127 * the same way that real requests are.
19129 * If there are multiple GET requests for the same URL that should be cached using the same
19130 * cache, but the cache is not populated yet, only one request to the server will be made and
19131 * the remaining requests will be fulfilled using the response from the first request.
19133 * You can change the default cache to a new object (built with
19134 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
19135 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
19136 * their `cache` property to `true` will now use this cache object.
19138 * If you set the default cache to `false` then only requests that specify their own custom
19139 * cache object will be cached.
19143 * Before you start creating interceptors, be sure to understand the
19144 * {@link ng.$q $q and deferred/promise APIs}.
19146 * For purposes of global error handling, authentication, or any kind of synchronous or
19147 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
19148 * able to intercept requests before they are handed to the server and
19149 * responses before they are handed over to the application code that
19150 * initiated these requests. The interceptors leverage the {@link ng.$q
19151 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
19153 * The interceptors are service factories that are registered with the `$httpProvider` by
19154 * adding them to the `$httpProvider.interceptors` array. The factory is called and
19155 * injected with dependencies (if specified) and returns the interceptor.
19157 * There are two kinds of interceptors (and two kinds of rejection interceptors):
19159 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
19160 * modify the `config` object or create a new one. The function needs to return the `config`
19161 * object directly, or a promise containing the `config` or a new `config` object.
19162 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
19163 * resolved with a rejection.
19164 * * `response`: interceptors get called with http `response` object. The function is free to
19165 * modify the `response` object or create a new one. The function needs to return the `response`
19166 * object directly, or as a promise containing the `response` or a new `response` object.
19167 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
19168 * resolved with a rejection.
19172 * // register the interceptor as a service
19173 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
19175 * // optional method
19176 * 'request': function(config) {
19177 * // do something on success
19181 * // optional method
19182 * 'requestError': function(rejection) {
19183 * // do something on error
19184 * if (canRecover(rejection)) {
19185 * return responseOrNewPromise
19187 * return $q.reject(rejection);
19192 * // optional method
19193 * 'response': function(response) {
19194 * // do something on success
19198 * // optional method
19199 * 'responseError': function(rejection) {
19200 * // do something on error
19201 * if (canRecover(rejection)) {
19202 * return responseOrNewPromise
19204 * return $q.reject(rejection);
19209 * $httpProvider.interceptors.push('myHttpInterceptor');
19212 * // alternatively, register the interceptor via an anonymous factory
19213 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
19215 * 'request': function(config) {
19219 * 'response': function(response) {
19226 * ## Security Considerations
19228 * When designing web applications, consider security threats from:
19230 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
19231 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
19233 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
19234 * pre-configured with strategies that address these issues, but for this to work backend server
19235 * cooperation is required.
19237 * ### JSON Vulnerability Protection
19239 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
19240 * allows third party website to turn your JSON resource URL into
19241 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
19242 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
19243 * Angular will automatically strip the prefix before processing it as JSON.
19245 * For example if your server needs to return:
19250 * which is vulnerable to attack, your server can return:
19256 * Angular will strip the prefix, before processing the JSON.
19259 * ### Cross Site Request Forgery (XSRF) Protection
19261 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
19262 * an unauthorized site can gain your user's private data. Angular provides a mechanism
19263 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
19264 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
19265 * JavaScript that runs on your domain could read the cookie, your server can be assured that
19266 * the XHR came from JavaScript running on your domain. The header will not be set for
19267 * cross-domain requests.
19269 * To take advantage of this, your server needs to set a token in a JavaScript readable session
19270 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
19271 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
19272 * that only JavaScript running on your domain could have sent the request. The token must be
19273 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
19274 * making up its own tokens). We recommend that the token is a digest of your site's
19275 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
19276 * for added security.
19278 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
19279 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
19280 * or the per-request config object.
19282 * In order to prevent collisions in environments where multiple Angular apps share the
19283 * same domain or subdomain, we recommend that each application uses unique cookie name.
19285 * @param {object} config Object describing the request to be made and how it should be
19286 * processed. The object has following properties:
19288 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
19289 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
19290 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
19291 * with the `paramSerializer` and appended as GET parameters.
19292 * - **data** – `{string|Object}` – Data to be sent as the request message data.
19293 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
19294 * HTTP headers to send to the server. If the return value of a function is null, the
19295 * header will not be sent. Functions accept a config object as an argument.
19296 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
19297 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
19298 * - **transformRequest** –
19299 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
19300 * transform function or an array of such functions. The transform function takes the http
19301 * request body and headers and returns its transformed (typically serialized) version.
19302 * See {@link ng.$http#overriding-the-default-transformations-per-request
19303 * Overriding the Default Transformations}
19304 * - **transformResponse** –
19305 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
19306 * transform function or an array of such functions. The transform function takes the http
19307 * response body, headers and status and returns its transformed (typically deserialized) version.
19308 * See {@link ng.$http#overriding-the-default-transformations-per-request
19309 * Overriding the Default TransformationjqLiks}
19310 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
19311 * prepare the string representation of request parameters (specified as an object).
19312 * If specified as string, it is interpreted as function registered with the
19313 * {@link $injector $injector}, which means you can create your own serializer
19314 * by registering it as a {@link auto.$provide#service service}.
19315 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
19316 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
19317 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
19318 * GET request, otherwise if a cache instance built with
19319 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
19321 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
19322 * that should abort the request when resolved.
19323 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
19324 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
19325 * for more information.
19326 * - **responseType** - `{string}` - see
19327 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
19329 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
19330 * when the request succeeds or fails.
19333 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
19334 * requests. This is primarily meant to be used for debugging purposes.
19338 <example module="httpExample">
19339 <file name="index.html">
19340 <div ng-controller="FetchController">
19341 <select ng-model="method" aria-label="Request method">
19342 <option>GET</option>
19343 <option>JSONP</option>
19345 <input type="text" ng-model="url" size="80" aria-label="URL" />
19346 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
19347 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
19348 <button id="samplejsonpbtn"
19349 ng-click="updateModel('JSONP',
19350 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
19353 <button id="invalidjsonpbtn"
19354 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
19357 <pre>http status code: {{status}}</pre>
19358 <pre>http response data: {{data}}</pre>
19361 <file name="script.js">
19362 angular.module('httpExample', [])
19363 .controller('FetchController', ['$scope', '$http', '$templateCache',
19364 function($scope, $http, $templateCache) {
19365 $scope.method = 'GET';
19366 $scope.url = 'http-hello.html';
19368 $scope.fetch = function() {
19369 $scope.code = null;
19370 $scope.response = null;
19372 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
19373 then(function(response) {
19374 $scope.status = response.status;
19375 $scope.data = response.data;
19376 }, function(response) {
19377 $scope.data = response.data || "Request failed";
19378 $scope.status = response.status;
19382 $scope.updateModel = function(method, url) {
19383 $scope.method = method;
19388 <file name="http-hello.html">
19391 <file name="protractor.js" type="protractor">
19392 var status = element(by.binding('status'));
19393 var data = element(by.binding('data'));
19394 var fetchBtn = element(by.id('fetchbtn'));
19395 var sampleGetBtn = element(by.id('samplegetbtn'));
19396 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
19397 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
19399 it('should make an xhr GET request', function() {
19400 sampleGetBtn.click();
19402 expect(status.getText()).toMatch('200');
19403 expect(data.getText()).toMatch(/Hello, \$http!/);
19406 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
19407 // it('should make a JSONP request to angularjs.org', function() {
19408 // sampleJsonpBtn.click();
19409 // fetchBtn.click();
19410 // expect(status.getText()).toMatch('200');
19411 // expect(data.getText()).toMatch(/Super Hero!/);
19414 it('should make JSONP request to invalid URL and invoke the error handler',
19416 invalidJsonpBtn.click();
19418 expect(status.getText()).toMatch('0');
19419 expect(data.getText()).toMatch('Request failed');
19424 function $http(requestConfig) {
19426 if (!angular.isObject(requestConfig)) {
19427 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
19430 var config = extend({
19432 transformRequest: defaults.transformRequest,
19433 transformResponse: defaults.transformResponse,
19434 paramSerializer: defaults.paramSerializer
19437 config.headers = mergeHeaders(requestConfig);
19438 config.method = uppercase(config.method);
19439 config.paramSerializer = isString(config.paramSerializer) ?
19440 $injector.get(config.paramSerializer) : config.paramSerializer;
19442 var serverRequest = function(config) {
19443 var headers = config.headers;
19444 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
19446 // strip content-type if data is undefined
19447 if (isUndefined(reqData)) {
19448 forEach(headers, function(value, header) {
19449 if (lowercase(header) === 'content-type') {
19450 delete headers[header];
19455 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
19456 config.withCredentials = defaults.withCredentials;
19460 return sendReq(config, reqData).then(transformResponse, transformResponse);
19463 var chain = [serverRequest, undefined];
19464 var promise = $q.when(config);
19466 // apply interceptors
19467 forEach(reversedInterceptors, function(interceptor) {
19468 if (interceptor.request || interceptor.requestError) {
19469 chain.unshift(interceptor.request, interceptor.requestError);
19471 if (interceptor.response || interceptor.responseError) {
19472 chain.push(interceptor.response, interceptor.responseError);
19476 while (chain.length) {
19477 var thenFn = chain.shift();
19478 var rejectFn = chain.shift();
19480 promise = promise.then(thenFn, rejectFn);
19483 if (useLegacyPromise) {
19484 promise.success = function(fn) {
19485 assertArgFn(fn, 'fn');
19487 promise.then(function(response) {
19488 fn(response.data, response.status, response.headers, config);
19493 promise.error = function(fn) {
19494 assertArgFn(fn, 'fn');
19496 promise.then(null, function(response) {
19497 fn(response.data, response.status, response.headers, config);
19502 promise.success = $httpMinErrLegacyFn('success');
19503 promise.error = $httpMinErrLegacyFn('error');
19508 function transformResponse(response) {
19509 // make a copy since the response must be cacheable
19510 var resp = extend({}, response);
19511 resp.data = transformData(response.data, response.headers, response.status,
19512 config.transformResponse);
19513 return (isSuccess(response.status))
19518 function executeHeaderFns(headers, config) {
19519 var headerContent, processedHeaders = {};
19521 forEach(headers, function(headerFn, header) {
19522 if (isFunction(headerFn)) {
19523 headerContent = headerFn(config);
19524 if (headerContent != null) {
19525 processedHeaders[header] = headerContent;
19528 processedHeaders[header] = headerFn;
19532 return processedHeaders;
19535 function mergeHeaders(config) {
19536 var defHeaders = defaults.headers,
19537 reqHeaders = extend({}, config.headers),
19538 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
19540 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
19542 // using for-in instead of forEach to avoid unecessary iteration after header has been found
19543 defaultHeadersIteration:
19544 for (defHeaderName in defHeaders) {
19545 lowercaseDefHeaderName = lowercase(defHeaderName);
19547 for (reqHeaderName in reqHeaders) {
19548 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
19549 continue defaultHeadersIteration;
19553 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
19556 // execute if header value is a function for merged headers
19557 return executeHeaderFns(reqHeaders, shallowCopy(config));
19561 $http.pendingRequests = [];
19568 * Shortcut method to perform `GET` request.
19570 * @param {string} url Relative or absolute URL specifying the destination of the request
19571 * @param {Object=} config Optional configuration object
19572 * @returns {HttpPromise} Future object
19577 * @name $http#delete
19580 * Shortcut method to perform `DELETE` request.
19582 * @param {string} url Relative or absolute URL specifying the destination of the request
19583 * @param {Object=} config Optional configuration object
19584 * @returns {HttpPromise} Future object
19592 * Shortcut method to perform `HEAD` request.
19594 * @param {string} url Relative or absolute URL specifying the destination of the request
19595 * @param {Object=} config Optional configuration object
19596 * @returns {HttpPromise} Future object
19601 * @name $http#jsonp
19604 * Shortcut method to perform `JSONP` request.
19606 * @param {string} url Relative or absolute URL specifying the destination of the request.
19607 * The name of the callback should be the string `JSON_CALLBACK`.
19608 * @param {Object=} config Optional configuration object
19609 * @returns {HttpPromise} Future object
19611 createShortMethods('get', 'delete', 'head', 'jsonp');
19618 * Shortcut method to perform `POST` request.
19620 * @param {string} url Relative or absolute URL specifying the destination of the request
19621 * @param {*} data Request content
19622 * @param {Object=} config Optional configuration object
19623 * @returns {HttpPromise} Future object
19631 * Shortcut method to perform `PUT` request.
19633 * @param {string} url Relative or absolute URL specifying the destination of the request
19634 * @param {*} data Request content
19635 * @param {Object=} config Optional configuration object
19636 * @returns {HttpPromise} Future object
19641 * @name $http#patch
19644 * Shortcut method to perform `PATCH` request.
19646 * @param {string} url Relative or absolute URL specifying the destination of the request
19647 * @param {*} data Request content
19648 * @param {Object=} config Optional configuration object
19649 * @returns {HttpPromise} Future object
19651 createShortMethodsWithData('post', 'put', 'patch');
19655 * @name $http#defaults
19658 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
19659 * default headers, withCredentials as well as request and response transformations.
19661 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
19663 $http.defaults = defaults;
19669 function createShortMethods(names) {
19670 forEach(arguments, function(name) {
19671 $http[name] = function(url, config) {
19672 return $http(extend({}, config || {}, {
19681 function createShortMethodsWithData(name) {
19682 forEach(arguments, function(name) {
19683 $http[name] = function(url, data, config) {
19684 return $http(extend({}, config || {}, {
19695 * Makes the request.
19697 * !!! ACCESSES CLOSURE VARS:
19698 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
19700 function sendReq(config, reqData) {
19701 var deferred = $q.defer(),
19702 promise = deferred.promise,
19705 reqHeaders = config.headers,
19706 url = buildUrl(config.url, config.paramSerializer(config.params));
19708 $http.pendingRequests.push(config);
19709 promise.then(removePendingReq, removePendingReq);
19712 if ((config.cache || defaults.cache) && config.cache !== false &&
19713 (config.method === 'GET' || config.method === 'JSONP')) {
19714 cache = isObject(config.cache) ? config.cache
19715 : isObject(defaults.cache) ? defaults.cache
19720 cachedResp = cache.get(url);
19721 if (isDefined(cachedResp)) {
19722 if (isPromiseLike(cachedResp)) {
19723 // cached request has already been sent, but there is no response yet
19724 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
19726 // serving from cache
19727 if (isArray(cachedResp)) {
19728 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
19730 resolvePromise(cachedResp, 200, {}, 'OK');
19734 // put the promise for the non-transformed response into cache as a placeholder
19735 cache.put(url, promise);
19740 // if we won't have the response in cache, set the xsrf headers and
19741 // send the request to the backend
19742 if (isUndefined(cachedResp)) {
19743 var xsrfValue = urlIsSameOrigin(config.url)
19744 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
19747 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
19750 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
19751 config.withCredentials, config.responseType);
19758 * Callback registered to $httpBackend():
19759 * - caches the response if desired
19760 * - resolves the raw $http promise
19763 function done(status, response, headersString, statusText) {
19765 if (isSuccess(status)) {
19766 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
19768 // remove promise from the cache
19773 function resolveHttpPromise() {
19774 resolvePromise(response, status, headersString, statusText);
19777 if (useApplyAsync) {
19778 $rootScope.$applyAsync(resolveHttpPromise);
19780 resolveHttpPromise();
19781 if (!$rootScope.$$phase) $rootScope.$apply();
19787 * Resolves the raw $http promise.
19789 function resolvePromise(response, status, headers, statusText) {
19790 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
19791 status = status >= -1 ? status : 0;
19793 (isSuccess(status) ? deferred.resolve : deferred.reject)({
19796 headers: headersGetter(headers),
19798 statusText: statusText
19802 function resolvePromiseWithResult(result) {
19803 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
19806 function removePendingReq() {
19807 var idx = $http.pendingRequests.indexOf(config);
19808 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
19813 function buildUrl(url, serializedParams) {
19814 if (serializedParams.length > 0) {
19815 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
19824 * @name $xhrFactory
19827 * Factory function used to create XMLHttpRequest objects.
19829 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
19832 * angular.module('myApp', [])
19833 * .factory('$xhrFactory', function() {
19834 * return function createXhr(method, url) {
19835 * return new window.XMLHttpRequest({mozSystem: true});
19840 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
19841 * @param {string} url URL of the request.
19843 function $xhrFactoryProvider() {
19844 this.$get = function() {
19845 return function createXhr() {
19846 return new window.XMLHttpRequest();
19853 * @name $httpBackend
19854 * @requires $window
19855 * @requires $document
19856 * @requires $xhrFactory
19859 * HTTP backend used by the {@link ng.$http service} that delegates to
19860 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
19862 * You should never need to use this service directly, instead use the higher-level abstractions:
19863 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
19865 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
19866 * $httpBackend} which can be trained with responses.
19868 function $HttpBackendProvider() {
19869 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
19870 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
19874 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
19875 // TODO(vojta): fix the signature
19876 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
19877 $browser.$$incOutstandingRequestCount();
19878 url = url || $browser.url();
19880 if (lowercase(method) == 'jsonp') {
19881 var callbackId = '_' + (callbacks.counter++).toString(36);
19882 callbacks[callbackId] = function(data) {
19883 callbacks[callbackId].data = data;
19884 callbacks[callbackId].called = true;
19887 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
19888 callbackId, function(status, text) {
19889 completeRequest(callback, status, callbacks[callbackId].data, "", text);
19890 callbacks[callbackId] = noop;
19894 var xhr = createXhr(method, url);
19896 xhr.open(method, url, true);
19897 forEach(headers, function(value, key) {
19898 if (isDefined(value)) {
19899 xhr.setRequestHeader(key, value);
19903 xhr.onload = function requestLoaded() {
19904 var statusText = xhr.statusText || '';
19906 // responseText is the old-school way of retrieving response (supported by IE9)
19907 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
19908 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
19910 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
19911 var status = xhr.status === 1223 ? 204 : xhr.status;
19913 // fix status code when it is 0 (0 status is undocumented).
19914 // Occurs when accessing file resources or on Android 4.1 stock browser
19915 // while retrieving files from application cache.
19916 if (status === 0) {
19917 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
19920 completeRequest(callback,
19923 xhr.getAllResponseHeaders(),
19927 var requestError = function() {
19928 // The response is always empty
19929 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
19930 completeRequest(callback, -1, null, null, '');
19933 xhr.onerror = requestError;
19934 xhr.onabort = requestError;
19936 if (withCredentials) {
19937 xhr.withCredentials = true;
19940 if (responseType) {
19942 xhr.responseType = responseType;
19944 // WebKit added support for the json responseType value on 09/03/2013
19945 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
19946 // known to throw when setting the value "json" as the response type. Other older
19947 // browsers implementing the responseType
19949 // The json response type can be ignored if not supported, because JSON payloads are
19950 // parsed on the client-side regardless.
19951 if (responseType !== 'json') {
19957 xhr.send(isUndefined(post) ? null : post);
19961 var timeoutId = $browserDefer(timeoutRequest, timeout);
19962 } else if (isPromiseLike(timeout)) {
19963 timeout.then(timeoutRequest);
19967 function timeoutRequest() {
19968 jsonpDone && jsonpDone();
19969 xhr && xhr.abort();
19972 function completeRequest(callback, status, response, headersString, statusText) {
19973 // cancel timeout and subsequent timeout promise resolution
19974 if (isDefined(timeoutId)) {
19975 $browserDefer.cancel(timeoutId);
19977 jsonpDone = xhr = null;
19979 callback(status, response, headersString, statusText);
19980 $browser.$$completeOutstandingRequest(noop);
19984 function jsonpReq(url, callbackId, done) {
19985 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
19986 // - fetches local scripts via XHR and evals them
19987 // - adds and immediately removes script elements from the document
19988 var script = rawDocument.createElement('script'), callback = null;
19989 script.type = "text/javascript";
19991 script.async = true;
19993 callback = function(event) {
19994 removeEventListenerFn(script, "load", callback);
19995 removeEventListenerFn(script, "error", callback);
19996 rawDocument.body.removeChild(script);
19999 var text = "unknown";
20002 if (event.type === "load" && !callbacks[callbackId].called) {
20003 event = { type: "error" };
20006 status = event.type === "error" ? 404 : 200;
20010 done(status, text);
20014 addEventListenerFn(script, "load", callback);
20015 addEventListenerFn(script, "error", callback);
20016 rawDocument.body.appendChild(script);
20021 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
20022 $interpolateMinErr.throwNoconcat = function(text) {
20023 throw $interpolateMinErr('noconcat',
20024 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
20025 "interpolations that concatenate multiple expressions when a trusted value is " +
20026 "required. See http://docs.angularjs.org/api/ng.$sce", text);
20029 $interpolateMinErr.interr = function(text, err) {
20030 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
20035 * @name $interpolateProvider
20039 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
20042 <example module="customInterpolationApp">
20043 <file name="index.html">
20045 var customInterpolationApp = angular.module('customInterpolationApp', []);
20047 customInterpolationApp.config(function($interpolateProvider) {
20048 $interpolateProvider.startSymbol('//');
20049 $interpolateProvider.endSymbol('//');
20053 customInterpolationApp.controller('DemoController', function() {
20054 this.label = "This binding is brought you by // interpolation symbols.";
20057 <div ng-app="App" ng-controller="DemoController as demo">
20061 <file name="protractor.js" type="protractor">
20062 it('should interpolate binding with custom symbols', function() {
20063 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
20068 function $InterpolateProvider() {
20069 var startSymbol = '{{';
20070 var endSymbol = '}}';
20074 * @name $interpolateProvider#startSymbol
20076 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
20078 * @param {string=} value new value to set the starting symbol to.
20079 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
20081 this.startSymbol = function(value) {
20083 startSymbol = value;
20086 return startSymbol;
20092 * @name $interpolateProvider#endSymbol
20094 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
20096 * @param {string=} value new value to set the ending symbol to.
20097 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
20099 this.endSymbol = function(value) {
20109 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
20110 var startSymbolLength = startSymbol.length,
20111 endSymbolLength = endSymbol.length,
20112 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
20113 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
20115 function escape(ch) {
20116 return '\\\\\\' + ch;
20119 function unescapeText(text) {
20120 return text.replace(escapedStartRegexp, startSymbol).
20121 replace(escapedEndRegexp, endSymbol);
20124 function stringify(value) {
20125 if (value == null) { // null || undefined
20128 switch (typeof value) {
20132 value = '' + value;
20135 value = toJson(value);
20143 * @name $interpolate
20151 * Compiles a string with markup into an interpolation function. This service is used by the
20152 * HTML {@link ng.$compile $compile} service for data binding. See
20153 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
20154 * interpolation markup.
20158 * var $interpolate = ...; // injected
20159 * var exp = $interpolate('Hello {{name | uppercase}}!');
20160 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
20163 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
20164 * `true`, the interpolation function will return `undefined` unless all embedded expressions
20165 * evaluate to a value other than `undefined`.
20168 * var $interpolate = ...; // injected
20169 * var context = {greeting: 'Hello', name: undefined };
20171 * // default "forgiving" mode
20172 * var exp = $interpolate('{{greeting}} {{name}}!');
20173 * expect(exp(context)).toEqual('Hello !');
20175 * // "allOrNothing" mode
20176 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
20177 * expect(exp(context)).toBeUndefined();
20178 * context.name = 'Angular';
20179 * expect(exp(context)).toEqual('Hello Angular!');
20182 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
20184 * ####Escaped Interpolation
20185 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
20186 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
20187 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
20190 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
20191 * degree, while also enabling code examples to work without relying on the
20192 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
20194 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
20195 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
20196 * interpolation start/end markers with their escaped counterparts.**
20198 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
20199 * output when the $interpolate service processes the text. So, for HTML elements interpolated
20200 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
20201 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
20202 * this is typically useful only when user-data is used in rendering a template from the server, or
20203 * when otherwise untrusted data is used by a directive.
20206 * <file name="index.html">
20207 * <div ng-init="username='A user'">
20208 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
20210 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
20211 * application, but fails to accomplish their task, because the server has correctly
20212 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
20214 * <p>Instead, the result of the attempted script injection is visible, and can be removed
20215 * from the database by an administrator.</p>
20220 * @param {string} text The text with markup to interpolate.
20221 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
20222 * embedded expression in order to return an interpolation function. Strings with no
20223 * embedded expression will return null for the interpolation function.
20224 * @param {string=} trustedContext when provided, the returned function passes the interpolated
20225 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
20226 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
20227 * provides Strict Contextual Escaping for details.
20228 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
20229 * unless all embedded expressions evaluate to a value other than `undefined`.
20230 * @returns {function(context)} an interpolation function which is used to compute the
20231 * interpolated string. The function has these parameters:
20233 * - `context`: evaluation context for all expressions embedded in the interpolated text
20235 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
20236 allOrNothing = !!allOrNothing;
20242 textLength = text.length,
20245 expressionPositions = [];
20247 while (index < textLength) {
20248 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
20249 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
20250 if (index !== startIndex) {
20251 concat.push(unescapeText(text.substring(index, startIndex)));
20253 exp = text.substring(startIndex + startSymbolLength, endIndex);
20254 expressions.push(exp);
20255 parseFns.push($parse(exp, parseStringifyInterceptor));
20256 index = endIndex + endSymbolLength;
20257 expressionPositions.push(concat.length);
20260 // we did not find an interpolation, so we have to add the remainder to the separators array
20261 if (index !== textLength) {
20262 concat.push(unescapeText(text.substring(index)));
20268 // Concatenating expressions makes it hard to reason about whether some combination of
20269 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
20270 // single expression be used for iframe[src], object[src], etc., we ensure that the value
20271 // that's used is assigned or constructed by some JS code somewhere that is more testable or
20272 // make it obvious that you bound the value to some user controlled value. This helps reduce
20273 // the load when auditing for XSS issues.
20274 if (trustedContext && concat.length > 1) {
20275 $interpolateMinErr.throwNoconcat(text);
20278 if (!mustHaveExpression || expressions.length) {
20279 var compute = function(values) {
20280 for (var i = 0, ii = expressions.length; i < ii; i++) {
20281 if (allOrNothing && isUndefined(values[i])) return;
20282 concat[expressionPositions[i]] = values[i];
20284 return concat.join('');
20287 var getValue = function(value) {
20288 return trustedContext ?
20289 $sce.getTrusted(trustedContext, value) :
20290 $sce.valueOf(value);
20293 return extend(function interpolationFn(context) {
20295 var ii = expressions.length;
20296 var values = new Array(ii);
20299 for (; i < ii; i++) {
20300 values[i] = parseFns[i](context);
20303 return compute(values);
20305 $exceptionHandler($interpolateMinErr.interr(text, err));
20309 // all of these properties are undocumented for now
20310 exp: text, //just for compatibility with regular watchers created via $watch
20311 expressions: expressions,
20312 $$watchDelegate: function(scope, listener) {
20314 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
20315 var currValue = compute(values);
20316 if (isFunction(listener)) {
20317 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
20319 lastValue = currValue;
20325 function parseStringifyInterceptor(value) {
20327 value = getValue(value);
20328 return allOrNothing && !isDefined(value) ? value : stringify(value);
20330 $exceptionHandler($interpolateMinErr.interr(text, err));
20338 * @name $interpolate#startSymbol
20340 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
20342 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
20345 * @returns {string} start symbol.
20347 $interpolate.startSymbol = function() {
20348 return startSymbol;
20354 * @name $interpolate#endSymbol
20356 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
20358 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
20361 * @returns {string} end symbol.
20363 $interpolate.endSymbol = function() {
20367 return $interpolate;
20371 function $IntervalProvider() {
20372 this.$get = ['$rootScope', '$window', '$q', '$$q',
20373 function($rootScope, $window, $q, $$q) {
20374 var intervals = {};
20382 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
20385 * The return value of registering an interval function is a promise. This promise will be
20386 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
20387 * run indefinitely if `count` is not defined. The value of the notification will be the
20388 * number of iterations that have run.
20389 * To cancel an interval, call `$interval.cancel(promise)`.
20391 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
20392 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
20395 * <div class="alert alert-warning">
20396 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
20397 * with them. In particular they are not automatically destroyed when a controller's scope or a
20398 * directive's element are destroyed.
20399 * You should take this into consideration and make sure to always cancel the interval at the
20400 * appropriate moment. See the example below for more details on how and when to do this.
20403 * @param {function()} fn A function that should be called repeatedly.
20404 * @param {number} delay Number of milliseconds between each function call.
20405 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
20407 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
20408 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
20409 * @param {...*=} Pass additional parameters to the executed function.
20410 * @returns {promise} A promise which will be notified on each iteration.
20413 * <example module="intervalExample">
20414 * <file name="index.html">
20416 * angular.module('intervalExample', [])
20417 * .controller('ExampleController', ['$scope', '$interval',
20418 * function($scope, $interval) {
20419 * $scope.format = 'M/d/yy h:mm:ss a';
20420 * $scope.blood_1 = 100;
20421 * $scope.blood_2 = 120;
20424 * $scope.fight = function() {
20425 * // Don't start a new fight if we are already fighting
20426 * if ( angular.isDefined(stop) ) return;
20428 * stop = $interval(function() {
20429 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
20430 * $scope.blood_1 = $scope.blood_1 - 3;
20431 * $scope.blood_2 = $scope.blood_2 - 4;
20433 * $scope.stopFight();
20438 * $scope.stopFight = function() {
20439 * if (angular.isDefined(stop)) {
20440 * $interval.cancel(stop);
20441 * stop = undefined;
20445 * $scope.resetFight = function() {
20446 * $scope.blood_1 = 100;
20447 * $scope.blood_2 = 120;
20450 * $scope.$on('$destroy', function() {
20451 * // Make sure that the interval is destroyed too
20452 * $scope.stopFight();
20455 * // Register the 'myCurrentTime' directive factory method.
20456 * // We inject $interval and dateFilter service since the factory method is DI.
20457 * .directive('myCurrentTime', ['$interval', 'dateFilter',
20458 * function($interval, dateFilter) {
20459 * // return the directive link function. (compile function not needed)
20460 * return function(scope, element, attrs) {
20461 * var format, // date format
20462 * stopTime; // so that we can cancel the time updates
20464 * // used to update the UI
20465 * function updateTime() {
20466 * element.text(dateFilter(new Date(), format));
20469 * // watch the expression, and update the UI on change.
20470 * scope.$watch(attrs.myCurrentTime, function(value) {
20475 * stopTime = $interval(updateTime, 1000);
20477 * // listen on DOM destroy (removal) event, and cancel the next UI update
20478 * // to prevent updating time after the DOM element was removed.
20479 * element.on('$destroy', function() {
20480 * $interval.cancel(stopTime);
20487 * <div ng-controller="ExampleController">
20488 * <label>Date format: <input ng-model="format"></label> <hr/>
20489 * Current time is: <span my-current-time="format"></span>
20491 * Blood 1 : <font color='red'>{{blood_1}}</font>
20492 * Blood 2 : <font color='red'>{{blood_2}}</font>
20493 * <button type="button" data-ng-click="fight()">Fight</button>
20494 * <button type="button" data-ng-click="stopFight()">StopFight</button>
20495 * <button type="button" data-ng-click="resetFight()">resetFight</button>
20502 function interval(fn, delay, count, invokeApply) {
20503 var hasParams = arguments.length > 4,
20504 args = hasParams ? sliceArgs(arguments, 4) : [],
20505 setInterval = $window.setInterval,
20506 clearInterval = $window.clearInterval,
20508 skipApply = (isDefined(invokeApply) && !invokeApply),
20509 deferred = (skipApply ? $$q : $q).defer(),
20510 promise = deferred.promise;
20512 count = isDefined(count) ? count : 0;
20514 promise.then(null, null, (!hasParams) ? fn : function() {
20515 fn.apply(null, args);
20518 promise.$$intervalId = setInterval(function tick() {
20519 deferred.notify(iteration++);
20521 if (count > 0 && iteration >= count) {
20522 deferred.resolve(iteration);
20523 clearInterval(promise.$$intervalId);
20524 delete intervals[promise.$$intervalId];
20527 if (!skipApply) $rootScope.$apply();
20531 intervals[promise.$$intervalId] = deferred;
20539 * @name $interval#cancel
20542 * Cancels a task associated with the `promise`.
20544 * @param {Promise=} promise returned by the `$interval` function.
20545 * @returns {boolean} Returns `true` if the task was successfully canceled.
20547 interval.cancel = function(promise) {
20548 if (promise && promise.$$intervalId in intervals) {
20549 intervals[promise.$$intervalId].reject('canceled');
20550 $window.clearInterval(promise.$$intervalId);
20551 delete intervals[promise.$$intervalId];
20566 * $locale service provides localization rules for various Angular components. As of right now the
20567 * only public api is:
20569 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
20572 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
20573 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
20574 var $locationMinErr = minErr('$location');
20578 * Encode path using encodeUriSegment, ignoring forward slashes
20580 * @param {string} path Path to encode
20581 * @returns {string}
20583 function encodePath(path) {
20584 var segments = path.split('/'),
20585 i = segments.length;
20588 segments[i] = encodeUriSegment(segments[i]);
20591 return segments.join('/');
20594 function parseAbsoluteUrl(absoluteUrl, locationObj) {
20595 var parsedUrl = urlResolve(absoluteUrl);
20597 locationObj.$$protocol = parsedUrl.protocol;
20598 locationObj.$$host = parsedUrl.hostname;
20599 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
20603 function parseAppUrl(relativeUrl, locationObj) {
20604 var prefixed = (relativeUrl.charAt(0) !== '/');
20606 relativeUrl = '/' + relativeUrl;
20608 var match = urlResolve(relativeUrl);
20609 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
20610 match.pathname.substring(1) : match.pathname);
20611 locationObj.$$search = parseKeyValue(match.search);
20612 locationObj.$$hash = decodeURIComponent(match.hash);
20614 // make sure path starts with '/';
20615 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
20616 locationObj.$$path = '/' + locationObj.$$path;
20623 * @param {string} begin
20624 * @param {string} whole
20625 * @returns {string} returns text from whole after begin or undefined if it does not begin with
20628 function beginsWith(begin, whole) {
20629 if (whole.indexOf(begin) === 0) {
20630 return whole.substr(begin.length);
20635 function stripHash(url) {
20636 var index = url.indexOf('#');
20637 return index == -1 ? url : url.substr(0, index);
20640 function trimEmptyHash(url) {
20641 return url.replace(/(#.+)|#$/, '$1');
20645 function stripFile(url) {
20646 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
20649 /* return the server only (scheme://host:port) */
20650 function serverBase(url) {
20651 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
20656 * LocationHtml5Url represents an url
20657 * This object is exposed as $location service when HTML5 mode is enabled and supported
20660 * @param {string} appBase application base URL
20661 * @param {string} appBaseNoFile application base URL stripped of any filename
20662 * @param {string} basePrefix url path prefix
20664 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
20665 this.$$html5 = true;
20666 basePrefix = basePrefix || '';
20667 parseAbsoluteUrl(appBase, this);
20671 * Parse given html5 (regular) url string into properties
20672 * @param {string} url HTML5 url
20675 this.$$parse = function(url) {
20676 var pathUrl = beginsWith(appBaseNoFile, url);
20677 if (!isString(pathUrl)) {
20678 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
20682 parseAppUrl(pathUrl, this);
20684 if (!this.$$path) {
20692 * Compose url and update `absUrl` property
20695 this.$$compose = function() {
20696 var search = toKeyValue(this.$$search),
20697 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
20699 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
20700 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
20703 this.$$parseLinkUrl = function(url, relHref) {
20704 if (relHref && relHref[0] === '#') {
20705 // special case for links to hash fragments:
20706 // keep the old url and only replace the hash fragment
20707 this.hash(relHref.slice(1));
20710 var appUrl, prevAppUrl;
20713 if (isDefined(appUrl = beginsWith(appBase, url))) {
20714 prevAppUrl = appUrl;
20715 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
20716 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
20718 rewrittenUrl = appBase + prevAppUrl;
20720 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
20721 rewrittenUrl = appBaseNoFile + appUrl;
20722 } else if (appBaseNoFile == url + '/') {
20723 rewrittenUrl = appBaseNoFile;
20725 if (rewrittenUrl) {
20726 this.$$parse(rewrittenUrl);
20728 return !!rewrittenUrl;
20734 * LocationHashbangUrl represents url
20735 * This object is exposed as $location service when developer doesn't opt into html5 mode.
20736 * It also serves as the base class for html5 mode fallback on legacy browsers.
20739 * @param {string} appBase application base URL
20740 * @param {string} appBaseNoFile application base URL stripped of any filename
20741 * @param {string} hashPrefix hashbang prefix
20743 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
20745 parseAbsoluteUrl(appBase, this);
20749 * Parse given hashbang url into properties
20750 * @param {string} url Hashbang url
20753 this.$$parse = function(url) {
20754 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
20755 var withoutHashUrl;
20757 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
20759 // The rest of the url starts with a hash so we have
20760 // got either a hashbang path or a plain hash fragment
20761 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
20762 if (isUndefined(withoutHashUrl)) {
20763 // There was no hashbang prefix so we just have a hash fragment
20764 withoutHashUrl = withoutBaseUrl;
20768 // There was no hashbang path nor hash fragment:
20769 // If we are in HTML5 mode we use what is left as the path;
20770 // Otherwise we ignore what is left
20771 if (this.$$html5) {
20772 withoutHashUrl = withoutBaseUrl;
20774 withoutHashUrl = '';
20775 if (isUndefined(withoutBaseUrl)) {
20782 parseAppUrl(withoutHashUrl, this);
20784 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
20789 * In Windows, on an anchor node on documents loaded from
20790 * the filesystem, the browser will return a pathname
20791 * prefixed with the drive name ('/C:/path') when a
20792 * pathname without a drive is set:
20793 * * a.setAttribute('href', '/foo')
20794 * * a.pathname === '/C:/foo' //true
20796 * Inside of Angular, we're always using pathnames that
20797 * do not include drive names for routing.
20799 function removeWindowsDriveName(path, url, base) {
20801 Matches paths for file protocol on windows,
20802 such as /C:/foo/bar, and captures only /foo/bar.
20804 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
20806 var firstPathSegmentMatch;
20808 //Get the relative path from the input URL.
20809 if (url.indexOf(base) === 0) {
20810 url = url.replace(base, '');
20813 // The input URL intentionally contains a first path segment that ends with a colon.
20814 if (windowsFilePathExp.exec(url)) {
20818 firstPathSegmentMatch = windowsFilePathExp.exec(path);
20819 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
20824 * Compose hashbang url and update `absUrl` property
20827 this.$$compose = function() {
20828 var search = toKeyValue(this.$$search),
20829 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
20831 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
20832 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
20835 this.$$parseLinkUrl = function(url, relHref) {
20836 if (stripHash(appBase) == stripHash(url)) {
20846 * LocationHashbangUrl represents url
20847 * This object is exposed as $location service when html5 history api is enabled but the browser
20848 * does not support it.
20851 * @param {string} appBase application base URL
20852 * @param {string} appBaseNoFile application base URL stripped of any filename
20853 * @param {string} hashPrefix hashbang prefix
20855 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
20856 this.$$html5 = true;
20857 LocationHashbangUrl.apply(this, arguments);
20859 this.$$parseLinkUrl = function(url, relHref) {
20860 if (relHref && relHref[0] === '#') {
20861 // special case for links to hash fragments:
20862 // keep the old url and only replace the hash fragment
20863 this.hash(relHref.slice(1));
20870 if (appBase == stripHash(url)) {
20871 rewrittenUrl = url;
20872 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
20873 rewrittenUrl = appBase + hashPrefix + appUrl;
20874 } else if (appBaseNoFile === url + '/') {
20875 rewrittenUrl = appBaseNoFile;
20877 if (rewrittenUrl) {
20878 this.$$parse(rewrittenUrl);
20880 return !!rewrittenUrl;
20883 this.$$compose = function() {
20884 var search = toKeyValue(this.$$search),
20885 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
20887 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
20888 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
20889 this.$$absUrl = appBase + hashPrefix + this.$$url;
20895 var locationPrototype = {
20898 * Are we in html5 mode?
20904 * Has any change been replacing?
20911 * @name $location#absUrl
20914 * This method is getter only.
20916 * Return full url representation with all segments encoded according to rules specified in
20917 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
20921 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
20922 * var absUrl = $location.absUrl();
20923 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
20926 * @return {string} full url
20928 absUrl: locationGetter('$$absUrl'),
20932 * @name $location#url
20935 * This method is getter / setter.
20937 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
20939 * Change path, search and hash, when called with parameter and return `$location`.
20943 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
20944 * var url = $location.url();
20945 * // => "/some/path?foo=bar&baz=xoxo"
20948 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
20949 * @return {string} url
20951 url: function(url) {
20952 if (isUndefined(url)) {
20956 var match = PATH_MATCH.exec(url);
20957 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
20958 if (match[2] || match[1] || url === '') this.search(match[3] || '');
20959 this.hash(match[5] || '');
20966 * @name $location#protocol
20969 * This method is getter only.
20971 * Return protocol of current url.
20975 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
20976 * var protocol = $location.protocol();
20980 * @return {string} protocol of current url
20982 protocol: locationGetter('$$protocol'),
20986 * @name $location#host
20989 * This method is getter only.
20991 * Return host of current url.
20993 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
20997 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
20998 * var host = $location.host();
20999 * // => "example.com"
21001 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
21002 * host = $location.host();
21003 * // => "example.com"
21004 * host = location.host;
21005 * // => "example.com:8080"
21008 * @return {string} host of current url.
21010 host: locationGetter('$$host'),
21014 * @name $location#port
21017 * This method is getter only.
21019 * Return port of current url.
21023 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21024 * var port = $location.port();
21028 * @return {Number} port
21030 port: locationGetter('$$port'),
21034 * @name $location#path
21037 * This method is getter / setter.
21039 * Return path of current url when called without any parameter.
21041 * Change path when called with parameter and return `$location`.
21043 * Note: Path should always begin with forward slash (/), this method will add the forward slash
21044 * if it is missing.
21048 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21049 * var path = $location.path();
21050 * // => "/some/path"
21053 * @param {(string|number)=} path New path
21054 * @return {string} path
21056 path: locationGetterSetter('$$path', function(path) {
21057 path = path !== null ? path.toString() : '';
21058 return path.charAt(0) == '/' ? path : '/' + path;
21063 * @name $location#search
21066 * This method is getter / setter.
21068 * Return search part (as object) of current url when called without any parameter.
21070 * Change search part when called with parameter and return `$location`.
21074 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21075 * var searchObject = $location.search();
21076 * // => {foo: 'bar', baz: 'xoxo'}
21078 * // set foo to 'yipee'
21079 * $location.search('foo', 'yipee');
21080 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
21083 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
21086 * When called with a single argument the method acts as a setter, setting the `search` component
21087 * of `$location` to the specified value.
21089 * If the argument is a hash object containing an array of values, these values will be encoded
21090 * as duplicate search parameters in the url.
21092 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
21093 * will override only a single search property.
21095 * If `paramValue` is an array, it will override the property of the `search` component of
21096 * `$location` specified via the first argument.
21098 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
21100 * If `paramValue` is `true`, the property specified via the first argument will be added with no
21101 * value nor trailing equal sign.
21103 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
21104 * one or more arguments returns `$location` object itself.
21106 search: function(search, paramValue) {
21107 switch (arguments.length) {
21109 return this.$$search;
21111 if (isString(search) || isNumber(search)) {
21112 search = search.toString();
21113 this.$$search = parseKeyValue(search);
21114 } else if (isObject(search)) {
21115 search = copy(search, {});
21116 // remove object undefined or null properties
21117 forEach(search, function(value, key) {
21118 if (value == null) delete search[key];
21121 this.$$search = search;
21123 throw $locationMinErr('isrcharg',
21124 'The first argument of the `$location#search()` call must be a string or an object.');
21128 if (isUndefined(paramValue) || paramValue === null) {
21129 delete this.$$search[search];
21131 this.$$search[search] = paramValue;
21141 * @name $location#hash
21144 * This method is getter / setter.
21146 * Returns the hash fragment when called without any parameters.
21148 * Changes the hash fragment when called with a parameter and returns `$location`.
21152 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
21153 * var hash = $location.hash();
21154 * // => "hashValue"
21157 * @param {(string|number)=} hash New hash fragment
21158 * @return {string} hash
21160 hash: locationGetterSetter('$$hash', function(hash) {
21161 return hash !== null ? hash.toString() : '';
21166 * @name $location#replace
21169 * If called, all changes to $location during the current `$digest` will replace the current history
21170 * record, instead of adding a new one.
21172 replace: function() {
21173 this.$$replace = true;
21178 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
21179 Location.prototype = Object.create(locationPrototype);
21183 * @name $location#state
21186 * This method is getter / setter.
21188 * Return the history state object when called without any parameter.
21190 * Change the history state object when called with one parameter and return `$location`.
21191 * The state object is later passed to `pushState` or `replaceState`.
21193 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
21194 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
21195 * older browsers (like IE9 or Android < 4.0), don't use this method.
21197 * @param {object=} state State object for pushState or replaceState
21198 * @return {object} state
21200 Location.prototype.state = function(state) {
21201 if (!arguments.length) {
21202 return this.$$state;
21205 if (Location !== LocationHtml5Url || !this.$$html5) {
21206 throw $locationMinErr('nostate', 'History API state support is available only ' +
21207 'in HTML5 mode and only in browsers supporting HTML5 History API');
21209 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
21210 // but we're changing the $$state reference to $browser.state() during the $digest
21211 // so the modification window is narrow.
21212 this.$$state = isUndefined(state) ? null : state;
21219 function locationGetter(property) {
21220 return function() {
21221 return this[property];
21226 function locationGetterSetter(property, preprocess) {
21227 return function(value) {
21228 if (isUndefined(value)) {
21229 return this[property];
21232 this[property] = preprocess(value);
21244 * @requires $rootElement
21247 * The $location service parses the URL in the browser address bar (based on the
21248 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
21249 * available to your application. Changes to the URL in the address bar are reflected into
21250 * $location service and changes to $location are reflected into the browser address bar.
21252 * **The $location service:**
21254 * - Exposes the current URL in the browser address bar, so you can
21255 * - Watch and observe the URL.
21256 * - Change the URL.
21257 * - Synchronizes the URL with the browser when the user
21258 * - Changes the address bar.
21259 * - Clicks the back or forward button (or clicks a History link).
21260 * - Clicks on a link.
21261 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
21263 * For more information see {@link guide/$location Developer Guide: Using $location}
21268 * @name $locationProvider
21270 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
21272 function $LocationProvider() {
21273 var hashPrefix = '',
21282 * @name $locationProvider#hashPrefix
21284 * @param {string=} prefix Prefix for hash part (containing path and search)
21285 * @returns {*} current value if used as getter or itself (chaining) if used as setter
21287 this.hashPrefix = function(prefix) {
21288 if (isDefined(prefix)) {
21289 hashPrefix = prefix;
21298 * @name $locationProvider#html5Mode
21300 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
21301 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
21303 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
21304 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
21305 * support `pushState`.
21306 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
21307 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
21308 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
21309 * See the {@link guide/$location $location guide for more information}
21310 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
21311 * enables/disables url rewriting for relative links.
21313 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
21315 this.html5Mode = function(mode) {
21316 if (isBoolean(mode)) {
21317 html5Mode.enabled = mode;
21319 } else if (isObject(mode)) {
21321 if (isBoolean(mode.enabled)) {
21322 html5Mode.enabled = mode.enabled;
21325 if (isBoolean(mode.requireBase)) {
21326 html5Mode.requireBase = mode.requireBase;
21329 if (isBoolean(mode.rewriteLinks)) {
21330 html5Mode.rewriteLinks = mode.rewriteLinks;
21341 * @name $location#$locationChangeStart
21342 * @eventType broadcast on root scope
21344 * Broadcasted before a URL will change.
21346 * This change can be prevented by calling
21347 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
21348 * details about event object. Upon successful change
21349 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
21351 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
21352 * the browser supports the HTML5 History API.
21354 * @param {Object} angularEvent Synthetic event object.
21355 * @param {string} newUrl New URL
21356 * @param {string=} oldUrl URL that was before it was changed.
21357 * @param {string=} newState New history state object
21358 * @param {string=} oldState History state object that was before it was changed.
21363 * @name $location#$locationChangeSuccess
21364 * @eventType broadcast on root scope
21366 * Broadcasted after a URL was changed.
21368 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
21369 * the browser supports the HTML5 History API.
21371 * @param {Object} angularEvent Synthetic event object.
21372 * @param {string} newUrl New URL
21373 * @param {string=} oldUrl URL that was before it was changed.
21374 * @param {string=} newState New history state object
21375 * @param {string=} oldState History state object that was before it was changed.
21378 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
21379 function($rootScope, $browser, $sniffer, $rootElement, $window) {
21382 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
21383 initialUrl = $browser.url(),
21386 if (html5Mode.enabled) {
21387 if (!baseHref && html5Mode.requireBase) {
21388 throw $locationMinErr('nobase',
21389 "$location in HTML5 mode requires a <base> tag to be present!");
21391 appBase = serverBase(initialUrl) + (baseHref || '/');
21392 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
21394 appBase = stripHash(initialUrl);
21395 LocationMode = LocationHashbangUrl;
21397 var appBaseNoFile = stripFile(appBase);
21399 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
21400 $location.$$parseLinkUrl(initialUrl, initialUrl);
21402 $location.$$state = $browser.state();
21404 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
21406 function setBrowserUrlWithFallback(url, replace, state) {
21407 var oldUrl = $location.url();
21408 var oldState = $location.$$state;
21410 $browser.url(url, replace, state);
21412 // Make sure $location.state() returns referentially identical (not just deeply equal)
21413 // state object; this makes possible quick checking if the state changed in the digest
21414 // loop. Checking deep equality would be too expensive.
21415 $location.$$state = $browser.state();
21417 // Restore old values if pushState fails
21418 $location.url(oldUrl);
21419 $location.$$state = oldState;
21425 $rootElement.on('click', function(event) {
21426 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
21427 // currently we open nice url link and redirect then
21429 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
21431 var elm = jqLite(event.target);
21433 // traverse the DOM up to find first A tag
21434 while (nodeName_(elm[0]) !== 'a') {
21435 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
21436 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
21439 var absHref = elm.prop('href');
21440 // get the actual href attribute - see
21441 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
21442 var relHref = elm.attr('href') || elm.attr('xlink:href');
21444 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
21445 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
21447 absHref = urlResolve(absHref.animVal).href;
21450 // Ignore when url is started with javascript: or mailto:
21451 if (IGNORE_URI_REGEXP.test(absHref)) return;
21453 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
21454 if ($location.$$parseLinkUrl(absHref, relHref)) {
21455 // We do a preventDefault for all urls that are part of the angular application,
21456 // in html5mode and also without, so that we are able to abort navigation without
21457 // getting double entries in the location history.
21458 event.preventDefault();
21459 // update location manually
21460 if ($location.absUrl() != $browser.url()) {
21461 $rootScope.$apply();
21462 // hack to work around FF6 bug 684208 when scenario runner clicks on links
21463 $window.angular['ff-684208-preventDefault'] = true;
21470 // rewrite hashbang url <> html5 url
21471 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
21472 $browser.url($location.absUrl(), true);
21475 var initializing = true;
21477 // update $location when $browser url changes
21478 $browser.onUrlChange(function(newUrl, newState) {
21480 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
21481 // If we are navigating outside of the app then force a reload
21482 $window.location.href = newUrl;
21486 $rootScope.$evalAsync(function() {
21487 var oldUrl = $location.absUrl();
21488 var oldState = $location.$$state;
21489 var defaultPrevented;
21490 newUrl = trimEmptyHash(newUrl);
21491 $location.$$parse(newUrl);
21492 $location.$$state = newState;
21494 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
21495 newState, oldState).defaultPrevented;
21497 // if the location was changed by a `$locationChangeStart` handler then stop
21498 // processing this location change
21499 if ($location.absUrl() !== newUrl) return;
21501 if (defaultPrevented) {
21502 $location.$$parse(oldUrl);
21503 $location.$$state = oldState;
21504 setBrowserUrlWithFallback(oldUrl, false, oldState);
21506 initializing = false;
21507 afterLocationChange(oldUrl, oldState);
21510 if (!$rootScope.$$phase) $rootScope.$digest();
21514 $rootScope.$watch(function $locationWatch() {
21515 var oldUrl = trimEmptyHash($browser.url());
21516 var newUrl = trimEmptyHash($location.absUrl());
21517 var oldState = $browser.state();
21518 var currentReplace = $location.$$replace;
21519 var urlOrStateChanged = oldUrl !== newUrl ||
21520 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
21522 if (initializing || urlOrStateChanged) {
21523 initializing = false;
21525 $rootScope.$evalAsync(function() {
21526 var newUrl = $location.absUrl();
21527 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
21528 $location.$$state, oldState).defaultPrevented;
21530 // if the location was changed by a `$locationChangeStart` handler then stop
21531 // processing this location change
21532 if ($location.absUrl() !== newUrl) return;
21534 if (defaultPrevented) {
21535 $location.$$parse(oldUrl);
21536 $location.$$state = oldState;
21538 if (urlOrStateChanged) {
21539 setBrowserUrlWithFallback(newUrl, currentReplace,
21540 oldState === $location.$$state ? null : $location.$$state);
21542 afterLocationChange(oldUrl, oldState);
21547 $location.$$replace = false;
21549 // we don't need to return anything because $evalAsync will make the digest loop dirty when
21550 // there is a change
21555 function afterLocationChange(oldUrl, oldState) {
21556 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
21557 $location.$$state, oldState);
21565 * @requires $window
21568 * Simple service for logging. Default implementation safely writes the message
21569 * into the browser's console (if present).
21571 * The main purpose of this service is to simplify debugging and troubleshooting.
21573 * The default is to log `debug` messages. You can use
21574 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
21577 <example module="logExample">
21578 <file name="script.js">
21579 angular.module('logExample', [])
21580 .controller('LogController', ['$scope', '$log', function($scope, $log) {
21581 $scope.$log = $log;
21582 $scope.message = 'Hello World!';
21585 <file name="index.html">
21586 <div ng-controller="LogController">
21587 <p>Reload this page with open console, enter text and hit the log button...</p>
21589 <input type="text" ng-model="message" /></label>
21590 <button ng-click="$log.log(message)">log</button>
21591 <button ng-click="$log.warn(message)">warn</button>
21592 <button ng-click="$log.info(message)">info</button>
21593 <button ng-click="$log.error(message)">error</button>
21594 <button ng-click="$log.debug(message)">debug</button>
21602 * @name $logProvider
21604 * Use the `$logProvider` to configure how the application logs messages
21606 function $LogProvider() {
21612 * @name $logProvider#debugEnabled
21614 * @param {boolean=} flag enable or disable debug level messages
21615 * @returns {*} current value if used as getter or itself (chaining) if used as setter
21617 this.debugEnabled = function(flag) {
21618 if (isDefined(flag)) {
21626 this.$get = ['$window', function($window) {
21633 * Write a log message
21635 log: consoleLog('log'),
21642 * Write an information message
21644 info: consoleLog('info'),
21651 * Write a warning message
21653 warn: consoleLog('warn'),
21660 * Write an error message
21662 error: consoleLog('error'),
21669 * Write a debug message
21671 debug: (function() {
21672 var fn = consoleLog('debug');
21674 return function() {
21676 fn.apply(self, arguments);
21682 function formatError(arg) {
21683 if (arg instanceof Error) {
21685 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
21686 ? 'Error: ' + arg.message + '\n' + arg.stack
21688 } else if (arg.sourceURL) {
21689 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
21695 function consoleLog(type) {
21696 var console = $window.console || {},
21697 logFn = console[type] || console.log || noop,
21700 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
21701 // The reason behind this is that console.log has type "object" in IE8...
21703 hasApply = !!logFn.apply;
21707 return function() {
21709 forEach(arguments, function(arg) {
21710 args.push(formatError(arg));
21712 return logFn.apply(console, args);
21716 // we are IE which either doesn't have window.console => this is noop and we do nothing,
21717 // or we are IE where console.log doesn't have apply so we log at least first 2 args
21718 return function(arg1, arg2) {
21719 logFn(arg1, arg2 == null ? '' : arg2);
21725 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21726 * Any commits to this file should be reviewed with security in mind. *
21727 * Changes to this file can potentially create security vulnerabilities. *
21728 * An approval from 2 Core members with history of modifying *
21729 * this file is required. *
21731 * Does the change somehow allow for arbitrary javascript to be executed? *
21732 * Or allows for someone to change the prototype of built-in objects? *
21733 * Or gives undesired access to variables likes document or window? *
21734 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21736 var $parseMinErr = minErr('$parse');
21738 // Sandboxing Angular Expressions
21739 // ------------------------------
21740 // Angular expressions are generally considered safe because these expressions only have direct
21741 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
21742 // obtaining a reference to native JS functions such as the Function constructor.
21744 // As an example, consider the following Angular expression:
21746 // {}.toString.constructor('alert("evil JS code")')
21748 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
21749 // against the expression language, but not to prevent exploits that were enabled by exposing
21750 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
21751 // practice and therefore we are not even trying to protect against interaction with an object
21752 // explicitly exposed in this way.
21754 // In general, it is not possible to access a Window object from an angular expression unless a
21755 // window or some DOM object that has a reference to window is published onto a Scope.
21756 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
21759 // See https://docs.angularjs.org/guide/security
21762 function ensureSafeMemberName(name, fullExpression) {
21763 if (name === "__defineGetter__" || name === "__defineSetter__"
21764 || name === "__lookupGetter__" || name === "__lookupSetter__"
21765 || name === "__proto__") {
21766 throw $parseMinErr('isecfld',
21767 'Attempting to access a disallowed field in Angular expressions! '
21768 + 'Expression: {0}', fullExpression);
21773 function getStringValue(name, fullExpression) {
21774 // From the JavaScript docs:
21775 // Property names must be strings. This means that non-string objects cannot be used
21776 // as keys in an object. Any non-string object, including a number, is typecasted
21777 // into a string via the toString method.
21779 // So, to ensure that we are checking the same `name` that JavaScript would use,
21780 // we cast it to a string, if possible.
21781 // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
21782 // this is, this will handle objects that misbehave.
21784 if (!isString(name)) {
21785 throw $parseMinErr('iseccst',
21786 'Cannot convert object to primitive value! '
21787 + 'Expression: {0}', fullExpression);
21792 function ensureSafeObject(obj, fullExpression) {
21793 // nifty check if obj is Function that is fast and works across iframes and other contexts
21795 if (obj.constructor === obj) {
21796 throw $parseMinErr('isecfn',
21797 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
21799 } else if (// isWindow(obj)
21800 obj.window === obj) {
21801 throw $parseMinErr('isecwindow',
21802 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
21804 } else if (// isElement(obj)
21805 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
21806 throw $parseMinErr('isecdom',
21807 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
21809 } else if (// block Object so that we can't get hold of dangerous Object.* methods
21811 throw $parseMinErr('isecobj',
21812 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
21819 var CALL = Function.prototype.call;
21820 var APPLY = Function.prototype.apply;
21821 var BIND = Function.prototype.bind;
21823 function ensureSafeFunction(obj, fullExpression) {
21825 if (obj.constructor === obj) {
21826 throw $parseMinErr('isecfn',
21827 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
21829 } else if (obj === CALL || obj === APPLY || obj === BIND) {
21830 throw $parseMinErr('isecff',
21831 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
21837 function ensureSafeAssignContext(obj, fullExpression) {
21839 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
21840 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
21841 throw $parseMinErr('isecaf',
21842 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
21847 var OPERATORS = createMap();
21848 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
21849 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
21852 /////////////////////////////////////////
21858 var Lexer = function(options) {
21859 this.options = options;
21862 Lexer.prototype = {
21863 constructor: Lexer,
21865 lex: function(text) {
21870 while (this.index < this.text.length) {
21871 var ch = this.text.charAt(this.index);
21872 if (ch === '"' || ch === "'") {
21873 this.readString(ch);
21874 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
21876 } else if (this.isIdent(ch)) {
21878 } else if (this.is(ch, '(){}[].,;:?')) {
21879 this.tokens.push({index: this.index, text: ch});
21881 } else if (this.isWhitespace(ch)) {
21884 var ch2 = ch + this.peek();
21885 var ch3 = ch2 + this.peek(2);
21886 var op1 = OPERATORS[ch];
21887 var op2 = OPERATORS[ch2];
21888 var op3 = OPERATORS[ch3];
21889 if (op1 || op2 || op3) {
21890 var token = op3 ? ch3 : (op2 ? ch2 : ch);
21891 this.tokens.push({index: this.index, text: token, operator: true});
21892 this.index += token.length;
21894 this.throwError('Unexpected next character ', this.index, this.index + 1);
21898 return this.tokens;
21901 is: function(ch, chars) {
21902 return chars.indexOf(ch) !== -1;
21905 peek: function(i) {
21907 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
21910 isNumber: function(ch) {
21911 return ('0' <= ch && ch <= '9') && typeof ch === "string";
21914 isWhitespace: function(ch) {
21915 // IE treats non-breaking space as \u00A0
21916 return (ch === ' ' || ch === '\r' || ch === '\t' ||
21917 ch === '\n' || ch === '\v' || ch === '\u00A0');
21920 isIdent: function(ch) {
21921 return ('a' <= ch && ch <= 'z' ||
21922 'A' <= ch && ch <= 'Z' ||
21923 '_' === ch || ch === '$');
21926 isExpOperator: function(ch) {
21927 return (ch === '-' || ch === '+' || this.isNumber(ch));
21930 throwError: function(error, start, end) {
21931 end = end || this.index;
21932 var colStr = (isDefined(start)
21933 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
21935 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
21936 error, colStr, this.text);
21939 readNumber: function() {
21941 var start = this.index;
21942 while (this.index < this.text.length) {
21943 var ch = lowercase(this.text.charAt(this.index));
21944 if (ch == '.' || this.isNumber(ch)) {
21947 var peekCh = this.peek();
21948 if (ch == 'e' && this.isExpOperator(peekCh)) {
21950 } else if (this.isExpOperator(ch) &&
21951 peekCh && this.isNumber(peekCh) &&
21952 number.charAt(number.length - 1) == 'e') {
21954 } else if (this.isExpOperator(ch) &&
21955 (!peekCh || !this.isNumber(peekCh)) &&
21956 number.charAt(number.length - 1) == 'e') {
21957 this.throwError('Invalid exponent');
21968 value: Number(number)
21972 readIdent: function() {
21973 var start = this.index;
21974 while (this.index < this.text.length) {
21975 var ch = this.text.charAt(this.index);
21976 if (!(this.isIdent(ch) || this.isNumber(ch))) {
21983 text: this.text.slice(start, this.index),
21988 readString: function(quote) {
21989 var start = this.index;
21992 var rawString = quote;
21993 var escape = false;
21994 while (this.index < this.text.length) {
21995 var ch = this.text.charAt(this.index);
21999 var hex = this.text.substring(this.index + 1, this.index + 5);
22000 if (!hex.match(/[\da-f]{4}/i)) {
22001 this.throwError('Invalid unicode escape [\\u' + hex + ']');
22004 string += String.fromCharCode(parseInt(hex, 16));
22006 var rep = ESCAPE[ch];
22007 string = string + (rep || ch);
22010 } else if (ch === '\\') {
22012 } else if (ch === quote) {
22026 this.throwError('Unterminated quote', start);
22030 var AST = function(lexer, options) {
22031 this.lexer = lexer;
22032 this.options = options;
22035 AST.Program = 'Program';
22036 AST.ExpressionStatement = 'ExpressionStatement';
22037 AST.AssignmentExpression = 'AssignmentExpression';
22038 AST.ConditionalExpression = 'ConditionalExpression';
22039 AST.LogicalExpression = 'LogicalExpression';
22040 AST.BinaryExpression = 'BinaryExpression';
22041 AST.UnaryExpression = 'UnaryExpression';
22042 AST.CallExpression = 'CallExpression';
22043 AST.MemberExpression = 'MemberExpression';
22044 AST.Identifier = 'Identifier';
22045 AST.Literal = 'Literal';
22046 AST.ArrayExpression = 'ArrayExpression';
22047 AST.Property = 'Property';
22048 AST.ObjectExpression = 'ObjectExpression';
22049 AST.ThisExpression = 'ThisExpression';
22051 // Internal use only
22052 AST.NGValueParameter = 'NGValueParameter';
22055 ast: function(text) {
22057 this.tokens = this.lexer.lex(text);
22059 var value = this.program();
22061 if (this.tokens.length !== 0) {
22062 this.throwError('is an unexpected token', this.tokens[0]);
22068 program: function() {
22071 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
22072 body.push(this.expressionStatement());
22073 if (!this.expect(';')) {
22074 return { type: AST.Program, body: body};
22079 expressionStatement: function() {
22080 return { type: AST.ExpressionStatement, expression: this.filterChain() };
22083 filterChain: function() {
22084 var left = this.expression();
22086 while ((token = this.expect('|'))) {
22087 left = this.filter(left);
22092 expression: function() {
22093 return this.assignment();
22096 assignment: function() {
22097 var result = this.ternary();
22098 if (this.expect('=')) {
22099 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
22104 ternary: function() {
22105 var test = this.logicalOR();
22108 if (this.expect('?')) {
22109 alternate = this.expression();
22110 if (this.consume(':')) {
22111 consequent = this.expression();
22112 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
22118 logicalOR: function() {
22119 var left = this.logicalAND();
22120 while (this.expect('||')) {
22121 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
22126 logicalAND: function() {
22127 var left = this.equality();
22128 while (this.expect('&&')) {
22129 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
22134 equality: function() {
22135 var left = this.relational();
22137 while ((token = this.expect('==','!=','===','!=='))) {
22138 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
22143 relational: function() {
22144 var left = this.additive();
22146 while ((token = this.expect('<', '>', '<=', '>='))) {
22147 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
22152 additive: function() {
22153 var left = this.multiplicative();
22155 while ((token = this.expect('+','-'))) {
22156 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
22161 multiplicative: function() {
22162 var left = this.unary();
22164 while ((token = this.expect('*','/','%'))) {
22165 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
22170 unary: function() {
22172 if ((token = this.expect('+', '-', '!'))) {
22173 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
22175 return this.primary();
22179 primary: function() {
22181 if (this.expect('(')) {
22182 primary = this.filterChain();
22184 } else if (this.expect('[')) {
22185 primary = this.arrayDeclaration();
22186 } else if (this.expect('{')) {
22187 primary = this.object();
22188 } else if (this.constants.hasOwnProperty(this.peek().text)) {
22189 primary = copy(this.constants[this.consume().text]);
22190 } else if (this.peek().identifier) {
22191 primary = this.identifier();
22192 } else if (this.peek().constant) {
22193 primary = this.constant();
22195 this.throwError('not a primary expression', this.peek());
22199 while ((next = this.expect('(', '[', '.'))) {
22200 if (next.text === '(') {
22201 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
22203 } else if (next.text === '[') {
22204 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
22206 } else if (next.text === '.') {
22207 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
22209 this.throwError('IMPOSSIBLE');
22215 filter: function(baseExpression) {
22216 var args = [baseExpression];
22217 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
22219 while (this.expect(':')) {
22220 args.push(this.expression());
22226 parseArguments: function() {
22228 if (this.peekToken().text !== ')') {
22230 args.push(this.expression());
22231 } while (this.expect(','));
22236 identifier: function() {
22237 var token = this.consume();
22238 if (!token.identifier) {
22239 this.throwError('is not a valid identifier', token);
22241 return { type: AST.Identifier, name: token.text };
22244 constant: function() {
22245 // TODO check that it is a constant
22246 return { type: AST.Literal, value: this.consume().value };
22249 arrayDeclaration: function() {
22251 if (this.peekToken().text !== ']') {
22253 if (this.peek(']')) {
22254 // Support trailing commas per ES5.1.
22257 elements.push(this.expression());
22258 } while (this.expect(','));
22262 return { type: AST.ArrayExpression, elements: elements };
22265 object: function() {
22266 var properties = [], property;
22267 if (this.peekToken().text !== '}') {
22269 if (this.peek('}')) {
22270 // Support trailing commas per ES5.1.
22273 property = {type: AST.Property, kind: 'init'};
22274 if (this.peek().constant) {
22275 property.key = this.constant();
22276 } else if (this.peek().identifier) {
22277 property.key = this.identifier();
22279 this.throwError("invalid key", this.peek());
22282 property.value = this.expression();
22283 properties.push(property);
22284 } while (this.expect(','));
22288 return {type: AST.ObjectExpression, properties: properties };
22291 throwError: function(msg, token) {
22292 throw $parseMinErr('syntax',
22293 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
22294 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
22297 consume: function(e1) {
22298 if (this.tokens.length === 0) {
22299 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
22302 var token = this.expect(e1);
22304 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
22309 peekToken: function() {
22310 if (this.tokens.length === 0) {
22311 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
22313 return this.tokens[0];
22316 peek: function(e1, e2, e3, e4) {
22317 return this.peekAhead(0, e1, e2, e3, e4);
22320 peekAhead: function(i, e1, e2, e3, e4) {
22321 if (this.tokens.length > i) {
22322 var token = this.tokens[i];
22323 var t = token.text;
22324 if (t === e1 || t === e2 || t === e3 || t === e4 ||
22325 (!e1 && !e2 && !e3 && !e4)) {
22332 expect: function(e1, e2, e3, e4) {
22333 var token = this.peek(e1, e2, e3, e4);
22335 this.tokens.shift();
22342 /* `undefined` is not a constant, it is an identifier,
22343 * but using it as an identifier is not supported
22346 'true': { type: AST.Literal, value: true },
22347 'false': { type: AST.Literal, value: false },
22348 'null': { type: AST.Literal, value: null },
22349 'undefined': {type: AST.Literal, value: undefined },
22350 'this': {type: AST.ThisExpression }
22354 function ifDefined(v, d) {
22355 return typeof v !== 'undefined' ? v : d;
22358 function plusFn(l, r) {
22359 if (typeof l === 'undefined') return r;
22360 if (typeof r === 'undefined') return l;
22364 function isStateless($filter, filterName) {
22365 var fn = $filter(filterName);
22366 return !fn.$stateful;
22369 function findConstantAndWatchExpressions(ast, $filter) {
22372 switch (ast.type) {
22374 allConstants = true;
22375 forEach(ast.body, function(expr) {
22376 findConstantAndWatchExpressions(expr.expression, $filter);
22377 allConstants = allConstants && expr.expression.constant;
22379 ast.constant = allConstants;
22382 ast.constant = true;
22385 case AST.UnaryExpression:
22386 findConstantAndWatchExpressions(ast.argument, $filter);
22387 ast.constant = ast.argument.constant;
22388 ast.toWatch = ast.argument.toWatch;
22390 case AST.BinaryExpression:
22391 findConstantAndWatchExpressions(ast.left, $filter);
22392 findConstantAndWatchExpressions(ast.right, $filter);
22393 ast.constant = ast.left.constant && ast.right.constant;
22394 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
22396 case AST.LogicalExpression:
22397 findConstantAndWatchExpressions(ast.left, $filter);
22398 findConstantAndWatchExpressions(ast.right, $filter);
22399 ast.constant = ast.left.constant && ast.right.constant;
22400 ast.toWatch = ast.constant ? [] : [ast];
22402 case AST.ConditionalExpression:
22403 findConstantAndWatchExpressions(ast.test, $filter);
22404 findConstantAndWatchExpressions(ast.alternate, $filter);
22405 findConstantAndWatchExpressions(ast.consequent, $filter);
22406 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
22407 ast.toWatch = ast.constant ? [] : [ast];
22409 case AST.Identifier:
22410 ast.constant = false;
22411 ast.toWatch = [ast];
22413 case AST.MemberExpression:
22414 findConstantAndWatchExpressions(ast.object, $filter);
22415 if (ast.computed) {
22416 findConstantAndWatchExpressions(ast.property, $filter);
22418 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
22419 ast.toWatch = [ast];
22421 case AST.CallExpression:
22422 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
22424 forEach(ast.arguments, function(expr) {
22425 findConstantAndWatchExpressions(expr, $filter);
22426 allConstants = allConstants && expr.constant;
22427 if (!expr.constant) {
22428 argsToWatch.push.apply(argsToWatch, expr.toWatch);
22431 ast.constant = allConstants;
22432 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
22434 case AST.AssignmentExpression:
22435 findConstantAndWatchExpressions(ast.left, $filter);
22436 findConstantAndWatchExpressions(ast.right, $filter);
22437 ast.constant = ast.left.constant && ast.right.constant;
22438 ast.toWatch = [ast];
22440 case AST.ArrayExpression:
22441 allConstants = true;
22443 forEach(ast.elements, function(expr) {
22444 findConstantAndWatchExpressions(expr, $filter);
22445 allConstants = allConstants && expr.constant;
22446 if (!expr.constant) {
22447 argsToWatch.push.apply(argsToWatch, expr.toWatch);
22450 ast.constant = allConstants;
22451 ast.toWatch = argsToWatch;
22453 case AST.ObjectExpression:
22454 allConstants = true;
22456 forEach(ast.properties, function(property) {
22457 findConstantAndWatchExpressions(property.value, $filter);
22458 allConstants = allConstants && property.value.constant;
22459 if (!property.value.constant) {
22460 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
22463 ast.constant = allConstants;
22464 ast.toWatch = argsToWatch;
22466 case AST.ThisExpression:
22467 ast.constant = false;
22473 function getInputs(body) {
22474 if (body.length != 1) return;
22475 var lastExpression = body[0].expression;
22476 var candidate = lastExpression.toWatch;
22477 if (candidate.length !== 1) return candidate;
22478 return candidate[0] !== lastExpression ? candidate : undefined;
22481 function isAssignable(ast) {
22482 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
22485 function assignableAST(ast) {
22486 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
22487 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
22491 function isLiteral(ast) {
22492 return ast.body.length === 0 ||
22493 ast.body.length === 1 && (
22494 ast.body[0].expression.type === AST.Literal ||
22495 ast.body[0].expression.type === AST.ArrayExpression ||
22496 ast.body[0].expression.type === AST.ObjectExpression);
22499 function isConstant(ast) {
22500 return ast.constant;
22503 function ASTCompiler(astBuilder, $filter) {
22504 this.astBuilder = astBuilder;
22505 this.$filter = $filter;
22508 ASTCompiler.prototype = {
22509 compile: function(expression, expensiveChecks) {
22511 var ast = this.astBuilder.ast(expression);
22515 expensiveChecks: expensiveChecks,
22516 fn: {vars: [], body: [], own: {}},
22517 assign: {vars: [], body: [], own: {}},
22520 findConstantAndWatchExpressions(ast, self.$filter);
22523 this.stage = 'assign';
22524 if ((assignable = assignableAST(ast))) {
22525 this.state.computing = 'assign';
22526 var result = this.nextId();
22527 this.recurse(assignable, result);
22528 this.return_(result);
22529 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
22531 var toWatch = getInputs(ast.body);
22532 self.stage = 'inputs';
22533 forEach(toWatch, function(watch, key) {
22534 var fnKey = 'fn' + key;
22535 self.state[fnKey] = {vars: [], body: [], own: {}};
22536 self.state.computing = fnKey;
22537 var intoId = self.nextId();
22538 self.recurse(watch, intoId);
22539 self.return_(intoId);
22540 self.state.inputs.push(fnKey);
22541 watch.watchId = key;
22543 this.state.computing = 'fn';
22544 this.stage = 'main';
22547 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
22548 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
22549 '"' + this.USE + ' ' + this.STRICT + '";\n' +
22550 this.filterPrefix() +
22551 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
22557 var fn = (new Function('$filter',
22558 'ensureSafeMemberName',
22559 'ensureSafeObject',
22560 'ensureSafeFunction',
22562 'ensureSafeAssignContext',
22568 ensureSafeMemberName,
22570 ensureSafeFunction,
22572 ensureSafeAssignContext,
22577 this.state = this.stage = undefined;
22578 fn.literal = isLiteral(ast);
22579 fn.constant = isConstant(ast);
22587 watchFns: function() {
22589 var fns = this.state.inputs;
22591 forEach(fns, function(name) {
22592 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
22595 result.push('fn.inputs=[' + fns.join(',') + '];');
22597 return result.join('');
22600 generateFunction: function(name, params) {
22601 return 'function(' + params + '){' +
22602 this.varsPrefix(name) +
22607 filterPrefix: function() {
22610 forEach(this.state.filters, function(id, filter) {
22611 parts.push(id + '=$filter(' + self.escape(filter) + ')');
22613 if (parts.length) return 'var ' + parts.join(',') + ';';
22617 varsPrefix: function(section) {
22618 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
22621 body: function(section) {
22622 return this.state[section].body.join('');
22625 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
22626 var left, right, self = this, args, expression;
22627 recursionFn = recursionFn || noop;
22628 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
22629 intoId = intoId || this.nextId();
22631 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
22632 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
22636 switch (ast.type) {
22638 forEach(ast.body, function(expression, pos) {
22639 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
22640 if (pos !== ast.body.length - 1) {
22641 self.current().body.push(right, ';');
22643 self.return_(right);
22648 expression = this.escape(ast.value);
22649 this.assign(intoId, expression);
22650 recursionFn(expression);
22652 case AST.UnaryExpression:
22653 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
22654 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
22655 this.assign(intoId, expression);
22656 recursionFn(expression);
22658 case AST.BinaryExpression:
22659 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
22660 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
22661 if (ast.operator === '+') {
22662 expression = this.plus(left, right);
22663 } else if (ast.operator === '-') {
22664 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
22666 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
22668 this.assign(intoId, expression);
22669 recursionFn(expression);
22671 case AST.LogicalExpression:
22672 intoId = intoId || this.nextId();
22673 self.recurse(ast.left, intoId);
22674 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
22675 recursionFn(intoId);
22677 case AST.ConditionalExpression:
22678 intoId = intoId || this.nextId();
22679 self.recurse(ast.test, intoId);
22680 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
22681 recursionFn(intoId);
22683 case AST.Identifier:
22684 intoId = intoId || this.nextId();
22686 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
22687 nameId.computed = false;
22688 nameId.name = ast.name;
22690 ensureSafeMemberName(ast.name);
22691 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
22693 self.if_(self.stage === 'inputs' || 's', function() {
22694 if (create && create !== 1) {
22696 self.not(self.nonComputedMember('s', ast.name)),
22697 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
22699 self.assign(intoId, self.nonComputedMember('s', ast.name));
22701 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
22703 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
22704 self.addEnsureSafeObject(intoId);
22706 recursionFn(intoId);
22708 case AST.MemberExpression:
22709 left = nameId && (nameId.context = this.nextId()) || this.nextId();
22710 intoId = intoId || this.nextId();
22711 self.recurse(ast.object, left, undefined, function() {
22712 self.if_(self.notNull(left), function() {
22713 if (ast.computed) {
22714 right = self.nextId();
22715 self.recurse(ast.property, right);
22716 self.getStringValue(right);
22717 self.addEnsureSafeMemberName(right);
22718 if (create && create !== 1) {
22719 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
22721 expression = self.ensureSafeObject(self.computedMember(left, right));
22722 self.assign(intoId, expression);
22724 nameId.computed = true;
22725 nameId.name = right;
22728 ensureSafeMemberName(ast.property.name);
22729 if (create && create !== 1) {
22730 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
22732 expression = self.nonComputedMember(left, ast.property.name);
22733 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
22734 expression = self.ensureSafeObject(expression);
22736 self.assign(intoId, expression);
22738 nameId.computed = false;
22739 nameId.name = ast.property.name;
22743 self.assign(intoId, 'undefined');
22745 recursionFn(intoId);
22748 case AST.CallExpression:
22749 intoId = intoId || this.nextId();
22751 right = self.filter(ast.callee.name);
22753 forEach(ast.arguments, function(expr) {
22754 var argument = self.nextId();
22755 self.recurse(expr, argument);
22756 args.push(argument);
22758 expression = right + '(' + args.join(',') + ')';
22759 self.assign(intoId, expression);
22760 recursionFn(intoId);
22762 right = self.nextId();
22765 self.recurse(ast.callee, right, left, function() {
22766 self.if_(self.notNull(right), function() {
22767 self.addEnsureSafeFunction(right);
22768 forEach(ast.arguments, function(expr) {
22769 self.recurse(expr, self.nextId(), undefined, function(argument) {
22770 args.push(self.ensureSafeObject(argument));
22774 if (!self.state.expensiveChecks) {
22775 self.addEnsureSafeObject(left.context);
22777 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
22779 expression = right + '(' + args.join(',') + ')';
22781 expression = self.ensureSafeObject(expression);
22782 self.assign(intoId, expression);
22784 self.assign(intoId, 'undefined');
22786 recursionFn(intoId);
22790 case AST.AssignmentExpression:
22791 right = this.nextId();
22793 if (!isAssignable(ast.left)) {
22794 throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
22796 this.recurse(ast.left, undefined, left, function() {
22797 self.if_(self.notNull(left.context), function() {
22798 self.recurse(ast.right, right);
22799 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
22800 self.addEnsureSafeAssignContext(left.context);
22801 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
22802 self.assign(intoId, expression);
22803 recursionFn(intoId || expression);
22807 case AST.ArrayExpression:
22809 forEach(ast.elements, function(expr) {
22810 self.recurse(expr, self.nextId(), undefined, function(argument) {
22811 args.push(argument);
22814 expression = '[' + args.join(',') + ']';
22815 this.assign(intoId, expression);
22816 recursionFn(expression);
22818 case AST.ObjectExpression:
22820 forEach(ast.properties, function(property) {
22821 self.recurse(property.value, self.nextId(), undefined, function(expr) {
22822 args.push(self.escape(
22823 property.key.type === AST.Identifier ? property.key.name :
22824 ('' + property.key.value)) +
22828 expression = '{' + args.join(',') + '}';
22829 this.assign(intoId, expression);
22830 recursionFn(expression);
22832 case AST.ThisExpression:
22833 this.assign(intoId, 's');
22836 case AST.NGValueParameter:
22837 this.assign(intoId, 'v');
22843 getHasOwnProperty: function(element, property) {
22844 var key = element + '.' + property;
22845 var own = this.current().own;
22846 if (!own.hasOwnProperty(key)) {
22847 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
22852 assign: function(id, value) {
22854 this.current().body.push(id, '=', value, ';');
22858 filter: function(filterName) {
22859 if (!this.state.filters.hasOwnProperty(filterName)) {
22860 this.state.filters[filterName] = this.nextId(true);
22862 return this.state.filters[filterName];
22865 ifDefined: function(id, defaultValue) {
22866 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
22869 plus: function(left, right) {
22870 return 'plus(' + left + ',' + right + ')';
22873 return_: function(id) {
22874 this.current().body.push('return ', id, ';');
22877 if_: function(test, alternate, consequent) {
22878 if (test === true) {
22881 var body = this.current().body;
22882 body.push('if(', test, '){');
22886 body.push('else{');
22893 not: function(expression) {
22894 return '!(' + expression + ')';
22897 notNull: function(expression) {
22898 return expression + '!=null';
22901 nonComputedMember: function(left, right) {
22902 return left + '.' + right;
22905 computedMember: function(left, right) {
22906 return left + '[' + right + ']';
22909 member: function(left, right, computed) {
22910 if (computed) return this.computedMember(left, right);
22911 return this.nonComputedMember(left, right);
22914 addEnsureSafeObject: function(item) {
22915 this.current().body.push(this.ensureSafeObject(item), ';');
22918 addEnsureSafeMemberName: function(item) {
22919 this.current().body.push(this.ensureSafeMemberName(item), ';');
22922 addEnsureSafeFunction: function(item) {
22923 this.current().body.push(this.ensureSafeFunction(item), ';');
22926 addEnsureSafeAssignContext: function(item) {
22927 this.current().body.push(this.ensureSafeAssignContext(item), ';');
22930 ensureSafeObject: function(item) {
22931 return 'ensureSafeObject(' + item + ',text)';
22934 ensureSafeMemberName: function(item) {
22935 return 'ensureSafeMemberName(' + item + ',text)';
22938 ensureSafeFunction: function(item) {
22939 return 'ensureSafeFunction(' + item + ',text)';
22942 getStringValue: function(item) {
22943 this.assign(item, 'getStringValue(' + item + ',text)');
22946 ensureSafeAssignContext: function(item) {
22947 return 'ensureSafeAssignContext(' + item + ',text)';
22950 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
22952 return function() {
22953 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
22957 lazyAssign: function(id, value) {
22959 return function() {
22960 self.assign(id, value);
22964 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
22966 stringEscapeFn: function(c) {
22967 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
22970 escape: function(value) {
22971 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
22972 if (isNumber(value)) return value.toString();
22973 if (value === true) return 'true';
22974 if (value === false) return 'false';
22975 if (value === null) return 'null';
22976 if (typeof value === 'undefined') return 'undefined';
22978 throw $parseMinErr('esc', 'IMPOSSIBLE');
22981 nextId: function(skip, init) {
22982 var id = 'v' + (this.state.nextId++);
22984 this.current().vars.push(id + (init ? '=' + init : ''));
22989 current: function() {
22990 return this.state[this.state.computing];
22995 function ASTInterpreter(astBuilder, $filter) {
22996 this.astBuilder = astBuilder;
22997 this.$filter = $filter;
23000 ASTInterpreter.prototype = {
23001 compile: function(expression, expensiveChecks) {
23003 var ast = this.astBuilder.ast(expression);
23004 this.expression = expression;
23005 this.expensiveChecks = expensiveChecks;
23006 findConstantAndWatchExpressions(ast, self.$filter);
23009 if ((assignable = assignableAST(ast))) {
23010 assign = this.recurse(assignable);
23012 var toWatch = getInputs(ast.body);
23016 forEach(toWatch, function(watch, key) {
23017 var input = self.recurse(watch);
23018 watch.input = input;
23019 inputs.push(input);
23020 watch.watchId = key;
23023 var expressions = [];
23024 forEach(ast.body, function(expression) {
23025 expressions.push(self.recurse(expression.expression));
23027 var fn = ast.body.length === 0 ? function() {} :
23028 ast.body.length === 1 ? expressions[0] :
23029 function(scope, locals) {
23031 forEach(expressions, function(exp) {
23032 lastValue = exp(scope, locals);
23037 fn.assign = function(scope, value, locals) {
23038 return assign(scope, locals, value);
23042 fn.inputs = inputs;
23044 fn.literal = isLiteral(ast);
23045 fn.constant = isConstant(ast);
23049 recurse: function(ast, context, create) {
23050 var left, right, self = this, args, expression;
23052 return this.inputs(ast.input, ast.watchId);
23054 switch (ast.type) {
23056 return this.value(ast.value, context);
23057 case AST.UnaryExpression:
23058 right = this.recurse(ast.argument);
23059 return this['unary' + ast.operator](right, context);
23060 case AST.BinaryExpression:
23061 left = this.recurse(ast.left);
23062 right = this.recurse(ast.right);
23063 return this['binary' + ast.operator](left, right, context);
23064 case AST.LogicalExpression:
23065 left = this.recurse(ast.left);
23066 right = this.recurse(ast.right);
23067 return this['binary' + ast.operator](left, right, context);
23068 case AST.ConditionalExpression:
23069 return this['ternary?:'](
23070 this.recurse(ast.test),
23071 this.recurse(ast.alternate),
23072 this.recurse(ast.consequent),
23075 case AST.Identifier:
23076 ensureSafeMemberName(ast.name, self.expression);
23077 return self.identifier(ast.name,
23078 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
23079 context, create, self.expression);
23080 case AST.MemberExpression:
23081 left = this.recurse(ast.object, false, !!create);
23082 if (!ast.computed) {
23083 ensureSafeMemberName(ast.property.name, self.expression);
23084 right = ast.property.name;
23086 if (ast.computed) right = this.recurse(ast.property);
23087 return ast.computed ?
23088 this.computedMember(left, right, context, create, self.expression) :
23089 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
23090 case AST.CallExpression:
23092 forEach(ast.arguments, function(expr) {
23093 args.push(self.recurse(expr));
23095 if (ast.filter) right = this.$filter(ast.callee.name);
23096 if (!ast.filter) right = this.recurse(ast.callee, true);
23097 return ast.filter ?
23098 function(scope, locals, assign, inputs) {
23100 for (var i = 0; i < args.length; ++i) {
23101 values.push(args[i](scope, locals, assign, inputs));
23103 var value = right.apply(undefined, values, inputs);
23104 return context ? {context: undefined, name: undefined, value: value} : value;
23106 function(scope, locals, assign, inputs) {
23107 var rhs = right(scope, locals, assign, inputs);
23109 if (rhs.value != null) {
23110 ensureSafeObject(rhs.context, self.expression);
23111 ensureSafeFunction(rhs.value, self.expression);
23113 for (var i = 0; i < args.length; ++i) {
23114 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
23116 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
23118 return context ? {value: value} : value;
23120 case AST.AssignmentExpression:
23121 left = this.recurse(ast.left, true, 1);
23122 right = this.recurse(ast.right);
23123 return function(scope, locals, assign, inputs) {
23124 var lhs = left(scope, locals, assign, inputs);
23125 var rhs = right(scope, locals, assign, inputs);
23126 ensureSafeObject(lhs.value, self.expression);
23127 ensureSafeAssignContext(lhs.context);
23128 lhs.context[lhs.name] = rhs;
23129 return context ? {value: rhs} : rhs;
23131 case AST.ArrayExpression:
23133 forEach(ast.elements, function(expr) {
23134 args.push(self.recurse(expr));
23136 return function(scope, locals, assign, inputs) {
23138 for (var i = 0; i < args.length; ++i) {
23139 value.push(args[i](scope, locals, assign, inputs));
23141 return context ? {value: value} : value;
23143 case AST.ObjectExpression:
23145 forEach(ast.properties, function(property) {
23146 args.push({key: property.key.type === AST.Identifier ?
23147 property.key.name :
23148 ('' + property.key.value),
23149 value: self.recurse(property.value)
23152 return function(scope, locals, assign, inputs) {
23154 for (var i = 0; i < args.length; ++i) {
23155 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
23157 return context ? {value: value} : value;
23159 case AST.ThisExpression:
23160 return function(scope) {
23161 return context ? {value: scope} : scope;
23163 case AST.NGValueParameter:
23164 return function(scope, locals, assign, inputs) {
23165 return context ? {value: assign} : assign;
23170 'unary+': function(argument, context) {
23171 return function(scope, locals, assign, inputs) {
23172 var arg = argument(scope, locals, assign, inputs);
23173 if (isDefined(arg)) {
23178 return context ? {value: arg} : arg;
23181 'unary-': function(argument, context) {
23182 return function(scope, locals, assign, inputs) {
23183 var arg = argument(scope, locals, assign, inputs);
23184 if (isDefined(arg)) {
23189 return context ? {value: arg} : arg;
23192 'unary!': function(argument, context) {
23193 return function(scope, locals, assign, inputs) {
23194 var arg = !argument(scope, locals, assign, inputs);
23195 return context ? {value: arg} : arg;
23198 'binary+': function(left, right, context) {
23199 return function(scope, locals, assign, inputs) {
23200 var lhs = left(scope, locals, assign, inputs);
23201 var rhs = right(scope, locals, assign, inputs);
23202 var arg = plusFn(lhs, rhs);
23203 return context ? {value: arg} : arg;
23206 'binary-': function(left, right, context) {
23207 return function(scope, locals, assign, inputs) {
23208 var lhs = left(scope, locals, assign, inputs);
23209 var rhs = right(scope, locals, assign, inputs);
23210 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
23211 return context ? {value: arg} : arg;
23214 'binary*': function(left, right, context) {
23215 return function(scope, locals, assign, inputs) {
23216 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
23217 return context ? {value: arg} : arg;
23220 'binary/': function(left, right, context) {
23221 return function(scope, locals, assign, inputs) {
23222 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
23223 return context ? {value: arg} : arg;
23226 'binary%': function(left, right, context) {
23227 return function(scope, locals, assign, inputs) {
23228 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
23229 return context ? {value: arg} : arg;
23232 'binary===': function(left, right, context) {
23233 return function(scope, locals, assign, inputs) {
23234 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
23235 return context ? {value: arg} : arg;
23238 'binary!==': function(left, right, context) {
23239 return function(scope, locals, assign, inputs) {
23240 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
23241 return context ? {value: arg} : arg;
23244 'binary==': function(left, right, context) {
23245 return function(scope, locals, assign, inputs) {
23246 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
23247 return context ? {value: arg} : arg;
23250 'binary!=': function(left, right, context) {
23251 return function(scope, locals, assign, inputs) {
23252 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
23253 return context ? {value: arg} : arg;
23256 'binary<': function(left, right, context) {
23257 return function(scope, locals, assign, inputs) {
23258 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
23259 return context ? {value: arg} : arg;
23262 'binary>': function(left, right, context) {
23263 return function(scope, locals, assign, inputs) {
23264 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
23265 return context ? {value: arg} : arg;
23268 'binary<=': function(left, right, context) {
23269 return function(scope, locals, assign, inputs) {
23270 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
23271 return context ? {value: arg} : arg;
23274 'binary>=': function(left, right, context) {
23275 return function(scope, locals, assign, inputs) {
23276 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
23277 return context ? {value: arg} : arg;
23280 'binary&&': function(left, right, context) {
23281 return function(scope, locals, assign, inputs) {
23282 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
23283 return context ? {value: arg} : arg;
23286 'binary||': function(left, right, context) {
23287 return function(scope, locals, assign, inputs) {
23288 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
23289 return context ? {value: arg} : arg;
23292 'ternary?:': function(test, alternate, consequent, context) {
23293 return function(scope, locals, assign, inputs) {
23294 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
23295 return context ? {value: arg} : arg;
23298 value: function(value, context) {
23299 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
23301 identifier: function(name, expensiveChecks, context, create, expression) {
23302 return function(scope, locals, assign, inputs) {
23303 var base = locals && (name in locals) ? locals : scope;
23304 if (create && create !== 1 && base && !(base[name])) {
23307 var value = base ? base[name] : undefined;
23308 if (expensiveChecks) {
23309 ensureSafeObject(value, expression);
23312 return {context: base, name: name, value: value};
23318 computedMember: function(left, right, context, create, expression) {
23319 return function(scope, locals, assign, inputs) {
23320 var lhs = left(scope, locals, assign, inputs);
23324 rhs = right(scope, locals, assign, inputs);
23325 rhs = getStringValue(rhs);
23326 ensureSafeMemberName(rhs, expression);
23327 if (create && create !== 1 && lhs && !(lhs[rhs])) {
23331 ensureSafeObject(value, expression);
23334 return {context: lhs, name: rhs, value: value};
23340 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
23341 return function(scope, locals, assign, inputs) {
23342 var lhs = left(scope, locals, assign, inputs);
23343 if (create && create !== 1 && lhs && !(lhs[right])) {
23346 var value = lhs != null ? lhs[right] : undefined;
23347 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
23348 ensureSafeObject(value, expression);
23351 return {context: lhs, name: right, value: value};
23357 inputs: function(input, watchId) {
23358 return function(scope, value, locals, inputs) {
23359 if (inputs) return inputs[watchId];
23360 return input(scope, value, locals);
23368 var Parser = function(lexer, $filter, options) {
23369 this.lexer = lexer;
23370 this.$filter = $filter;
23371 this.options = options;
23372 this.ast = new AST(this.lexer);
23373 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
23374 new ASTCompiler(this.ast, $filter);
23377 Parser.prototype = {
23378 constructor: Parser,
23380 parse: function(text) {
23381 return this.astCompiler.compile(text, this.options.expensiveChecks);
23385 var getterFnCacheDefault = createMap();
23386 var getterFnCacheExpensive = createMap();
23388 function isPossiblyDangerousMemberName(name) {
23389 return name == 'constructor';
23392 var objectValueOf = Object.prototype.valueOf;
23394 function getValueOf(value) {
23395 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
23398 ///////////////////////////////////
23407 * Converts Angular {@link guide/expression expression} into a function.
23410 * var getter = $parse('user.name');
23411 * var setter = getter.assign;
23412 * var context = {user:{name:'angular'}};
23413 * var locals = {user:{name:'local'}};
23415 * expect(getter(context)).toEqual('angular');
23416 * setter(context, 'newValue');
23417 * expect(context.user.name).toEqual('newValue');
23418 * expect(getter(context, locals)).toEqual('local');
23422 * @param {string} expression String expression to compile.
23423 * @returns {function(context, locals)} a function which represents the compiled expression:
23425 * * `context` – `{object}` – an object against which any expressions embedded in the strings
23426 * are evaluated against (typically a scope object).
23427 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
23430 * The returned function also has the following properties:
23431 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
23433 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
23434 * constant literals.
23435 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
23436 * set to a function to change its value on the given context.
23443 * @name $parseProvider
23446 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
23449 function $ParseProvider() {
23450 var cacheDefault = createMap();
23451 var cacheExpensive = createMap();
23453 this.$get = ['$filter', function($filter) {
23454 var noUnsafeEval = csp().noUnsafeEval;
23455 var $parseOptions = {
23457 expensiveChecks: false
23459 $parseOptionsExpensive = {
23461 expensiveChecks: true
23464 return function $parse(exp, interceptorFn, expensiveChecks) {
23465 var parsedExpression, oneTime, cacheKey;
23467 switch (typeof exp) {
23472 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
23473 parsedExpression = cache[cacheKey];
23475 if (!parsedExpression) {
23476 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
23478 exp = exp.substring(2);
23480 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
23481 var lexer = new Lexer(parseOptions);
23482 var parser = new Parser(lexer, $filter, parseOptions);
23483 parsedExpression = parser.parse(exp);
23484 if (parsedExpression.constant) {
23485 parsedExpression.$$watchDelegate = constantWatchDelegate;
23486 } else if (oneTime) {
23487 parsedExpression.$$watchDelegate = parsedExpression.literal ?
23488 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
23489 } else if (parsedExpression.inputs) {
23490 parsedExpression.$$watchDelegate = inputsWatchDelegate;
23492 cache[cacheKey] = parsedExpression;
23494 return addInterceptor(parsedExpression, interceptorFn);
23497 return addInterceptor(exp, interceptorFn);
23504 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
23506 if (newValue == null || oldValueOfValue == null) { // null/undefined
23507 return newValue === oldValueOfValue;
23510 if (typeof newValue === 'object') {
23512 // attempt to convert the value to a primitive type
23513 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
23514 // be cheaply dirty-checked
23515 newValue = getValueOf(newValue);
23517 if (typeof newValue === 'object') {
23518 // objects/arrays are not supported - deep-watching them would be too expensive
23522 // fall-through to the primitive equality check
23526 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
23529 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
23530 var inputExpressions = parsedExpression.inputs;
23533 if (inputExpressions.length === 1) {
23534 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
23535 inputExpressions = inputExpressions[0];
23536 return scope.$watch(function expressionInputWatch(scope) {
23537 var newInputValue = inputExpressions(scope);
23538 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
23539 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
23540 oldInputValueOf = newInputValue && getValueOf(newInputValue);
23543 }, listener, objectEquality, prettyPrintExpression);
23546 var oldInputValueOfValues = [];
23547 var oldInputValues = [];
23548 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
23549 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
23550 oldInputValues[i] = null;
23553 return scope.$watch(function expressionInputsWatch(scope) {
23554 var changed = false;
23556 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
23557 var newInputValue = inputExpressions[i](scope);
23558 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
23559 oldInputValues[i] = newInputValue;
23560 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
23565 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
23569 }, listener, objectEquality, prettyPrintExpression);
23572 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
23573 var unwatch, lastValue;
23574 return unwatch = scope.$watch(function oneTimeWatch(scope) {
23575 return parsedExpression(scope);
23576 }, function oneTimeListener(value, old, scope) {
23578 if (isFunction(listener)) {
23579 listener.apply(this, arguments);
23581 if (isDefined(value)) {
23582 scope.$$postDigest(function() {
23583 if (isDefined(lastValue)) {
23588 }, objectEquality);
23591 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
23592 var unwatch, lastValue;
23593 return unwatch = scope.$watch(function oneTimeWatch(scope) {
23594 return parsedExpression(scope);
23595 }, function oneTimeListener(value, old, scope) {
23597 if (isFunction(listener)) {
23598 listener.call(this, value, old, scope);
23600 if (isAllDefined(value)) {
23601 scope.$$postDigest(function() {
23602 if (isAllDefined(lastValue)) unwatch();
23605 }, objectEquality);
23607 function isAllDefined(value) {
23608 var allDefined = true;
23609 forEach(value, function(val) {
23610 if (!isDefined(val)) allDefined = false;
23616 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
23618 return unwatch = scope.$watch(function constantWatch(scope) {
23619 return parsedExpression(scope);
23620 }, function constantListener(value, old, scope) {
23621 if (isFunction(listener)) {
23622 listener.apply(this, arguments);
23625 }, objectEquality);
23628 function addInterceptor(parsedExpression, interceptorFn) {
23629 if (!interceptorFn) return parsedExpression;
23630 var watchDelegate = parsedExpression.$$watchDelegate;
23631 var useInputs = false;
23634 watchDelegate !== oneTimeLiteralWatchDelegate &&
23635 watchDelegate !== oneTimeWatchDelegate;
23637 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
23638 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
23639 return interceptorFn(value, scope, locals);
23640 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
23641 var value = parsedExpression(scope, locals, assign, inputs);
23642 var result = interceptorFn(value, scope, locals);
23643 // we only return the interceptor's result if the
23644 // initial value is defined (for bind-once)
23645 return isDefined(value) ? result : value;
23648 // Propagate $$watchDelegates other then inputsWatchDelegate
23649 if (parsedExpression.$$watchDelegate &&
23650 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
23651 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
23652 } else if (!interceptorFn.$stateful) {
23653 // If there is an interceptor, but no watchDelegate then treat the interceptor like
23654 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
23655 fn.$$watchDelegate = inputsWatchDelegate;
23656 useInputs = !parsedExpression.inputs;
23657 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
23668 * @requires $rootScope
23671 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
23672 * when they are done processing.
23674 * This is an implementation of promises/deferred objects inspired by
23675 * [Kris Kowal's Q](https://github.com/kriskowal/q).
23677 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
23678 * implementations, and the other which resembles ES6 promises to some degree.
23682 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
23683 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
23684 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
23686 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
23689 * It can be used like so:
23692 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
23693 * // are available in the current lexical scope (they could have been injected or passed in).
23695 * function asyncGreet(name) {
23696 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
23697 * return $q(function(resolve, reject) {
23698 * setTimeout(function() {
23699 * if (okToGreet(name)) {
23700 * resolve('Hello, ' + name + '!');
23702 * reject('Greeting ' + name + ' is not allowed.');
23708 * var promise = asyncGreet('Robin Hood');
23709 * promise.then(function(greeting) {
23710 * alert('Success: ' + greeting);
23711 * }, function(reason) {
23712 * alert('Failed: ' + reason);
23716 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
23718 * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
23720 * However, the more traditional CommonJS-style usage is still available, and documented below.
23722 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
23723 * interface for interacting with an object that represents the result of an action that is
23724 * performed asynchronously, and may or may not be finished at any given point in time.
23726 * From the perspective of dealing with error handling, deferred and promise APIs are to
23727 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
23730 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
23731 * // are available in the current lexical scope (they could have been injected or passed in).
23733 * function asyncGreet(name) {
23734 * var deferred = $q.defer();
23736 * setTimeout(function() {
23737 * deferred.notify('About to greet ' + name + '.');
23739 * if (okToGreet(name)) {
23740 * deferred.resolve('Hello, ' + name + '!');
23742 * deferred.reject('Greeting ' + name + ' is not allowed.');
23746 * return deferred.promise;
23749 * var promise = asyncGreet('Robin Hood');
23750 * promise.then(function(greeting) {
23751 * alert('Success: ' + greeting);
23752 * }, function(reason) {
23753 * alert('Failed: ' + reason);
23754 * }, function(update) {
23755 * alert('Got notification: ' + update);
23759 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
23760 * comes in the way of guarantees that promise and deferred APIs make, see
23761 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
23763 * Additionally the promise api allows for composition that is very hard to do with the
23764 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
23765 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
23766 * section on serial or parallel joining of promises.
23768 * # The Deferred API
23770 * A new instance of deferred is constructed by calling `$q.defer()`.
23772 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
23773 * that can be used for signaling the successful or unsuccessful completion, as well as the status
23778 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
23779 * constructed via `$q.reject`, the promise will be rejected instead.
23780 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
23781 * resolving it with a rejection constructed via `$q.reject`.
23782 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
23783 * multiple times before the promise is either resolved or rejected.
23787 * - promise – `{Promise}` – promise object associated with this deferred.
23790 * # The Promise API
23792 * A new promise instance is created when a deferred instance is created and can be retrieved by
23793 * calling `deferred.promise`.
23795 * The purpose of the promise object is to allow for interested parties to get access to the result
23796 * of the deferred task when it completes.
23800 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
23801 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
23802 * as soon as the result is available. The callbacks are called with a single argument: the result
23803 * or rejection reason. Additionally, the notify callback may be called zero or more times to
23804 * provide a progress indication, before the promise is resolved or rejected.
23806 * This method *returns a new promise* which is resolved or rejected via the return value of the
23807 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
23808 * with the value which is resolved in that promise using
23809 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
23810 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
23811 * resolved or rejected from the notifyCallback method.
23813 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
23815 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
23816 * but to do so without modifying the final value. This is useful to release resources or do some
23817 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
23818 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
23819 * more information.
23821 * # Chaining promises
23823 * Because calling the `then` method of a promise returns a new derived promise, it is easily
23824 * possible to create a chain of promises:
23827 * promiseB = promiseA.then(function(result) {
23828 * return result + 1;
23831 * // promiseB will be resolved immediately after promiseA is resolved and its value
23832 * // will be the result of promiseA incremented by 1
23835 * It is possible to create chains of any length and since a promise can be resolved with another
23836 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
23837 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
23838 * $http's response interceptors.
23841 * # Differences between Kris Kowal's Q and $q
23843 * There are two main differences:
23845 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
23846 * mechanism in angular, which means faster propagation of resolution or rejection into your
23847 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
23848 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
23849 * all the important functionality needed for common async tasks.
23854 * it('should simulate promise', inject(function($q, $rootScope) {
23855 * var deferred = $q.defer();
23856 * var promise = deferred.promise;
23857 * var resolvedValue;
23859 * promise.then(function(value) { resolvedValue = value; });
23860 * expect(resolvedValue).toBeUndefined();
23862 * // Simulate resolving of promise
23863 * deferred.resolve(123);
23864 * // Note that the 'then' function does not get called synchronously.
23865 * // This is because we want the promise API to always be async, whether or not
23866 * // it got called synchronously or asynchronously.
23867 * expect(resolvedValue).toBeUndefined();
23869 * // Propagate promise resolution to 'then' functions using $apply().
23870 * $rootScope.$apply();
23871 * expect(resolvedValue).toEqual(123);
23875 * @param {function(function, function)} resolver Function which is responsible for resolving or
23876 * rejecting the newly created promise. The first parameter is a function which resolves the
23877 * promise, the second parameter is a function which rejects the promise.
23879 * @returns {Promise} The newly created promise.
23881 function $QProvider() {
23883 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
23884 return qFactory(function(callback) {
23885 $rootScope.$evalAsync(callback);
23886 }, $exceptionHandler);
23890 function $$QProvider() {
23891 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
23892 return qFactory(function(callback) {
23893 $browser.defer(callback);
23894 }, $exceptionHandler);
23899 * Constructs a promise manager.
23901 * @param {function(function)} nextTick Function for executing functions in the next turn.
23902 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
23903 * debugging purposes.
23904 * @returns {object} Promise manager.
23906 function qFactory(nextTick, exceptionHandler) {
23907 var $qMinErr = minErr('$q', TypeError);
23908 function callOnce(self, resolveFn, rejectFn) {
23909 var called = false;
23910 function wrap(fn) {
23911 return function(value) {
23912 if (called) return;
23914 fn.call(self, value);
23918 return [wrap(resolveFn), wrap(rejectFn)];
23923 * @name ng.$q#defer
23927 * Creates a `Deferred` object which represents a task which will finish in the future.
23929 * @returns {Deferred} Returns a new instance of deferred.
23931 var defer = function() {
23932 return new Deferred();
23935 function Promise() {
23936 this.$$state = { status: 0 };
23939 extend(Promise.prototype, {
23940 then: function(onFulfilled, onRejected, progressBack) {
23941 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
23944 var result = new Deferred();
23946 this.$$state.pending = this.$$state.pending || [];
23947 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
23948 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
23950 return result.promise;
23953 "catch": function(callback) {
23954 return this.then(null, callback);
23957 "finally": function(callback, progressBack) {
23958 return this.then(function(value) {
23959 return handleCallback(value, true, callback);
23960 }, function(error) {
23961 return handleCallback(error, false, callback);
23966 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
23967 function simpleBind(context, fn) {
23968 return function(value) {
23969 fn.call(context, value);
23973 function processQueue(state) {
23974 var fn, deferred, pending;
23976 pending = state.pending;
23977 state.processScheduled = false;
23978 state.pending = undefined;
23979 for (var i = 0, ii = pending.length; i < ii; ++i) {
23980 deferred = pending[i][0];
23981 fn = pending[i][state.status];
23983 if (isFunction(fn)) {
23984 deferred.resolve(fn(state.value));
23985 } else if (state.status === 1) {
23986 deferred.resolve(state.value);
23988 deferred.reject(state.value);
23991 deferred.reject(e);
23992 exceptionHandler(e);
23997 function scheduleProcessQueue(state) {
23998 if (state.processScheduled || !state.pending) return;
23999 state.processScheduled = true;
24000 nextTick(function() { processQueue(state); });
24003 function Deferred() {
24004 this.promise = new Promise();
24005 //Necessary to support unbound execution :/
24006 this.resolve = simpleBind(this, this.resolve);
24007 this.reject = simpleBind(this, this.reject);
24008 this.notify = simpleBind(this, this.notify);
24011 extend(Deferred.prototype, {
24012 resolve: function(val) {
24013 if (this.promise.$$state.status) return;
24014 if (val === this.promise) {
24015 this.$$reject($qMinErr(
24017 "Expected promise to be resolved with value other than itself '{0}'",
24020 this.$$resolve(val);
24025 $$resolve: function(val) {
24028 fns = callOnce(this, this.$$resolve, this.$$reject);
24030 if ((isObject(val) || isFunction(val))) then = val && val.then;
24031 if (isFunction(then)) {
24032 this.promise.$$state.status = -1;
24033 then.call(val, fns[0], fns[1], this.notify);
24035 this.promise.$$state.value = val;
24036 this.promise.$$state.status = 1;
24037 scheduleProcessQueue(this.promise.$$state);
24041 exceptionHandler(e);
24045 reject: function(reason) {
24046 if (this.promise.$$state.status) return;
24047 this.$$reject(reason);
24050 $$reject: function(reason) {
24051 this.promise.$$state.value = reason;
24052 this.promise.$$state.status = 2;
24053 scheduleProcessQueue(this.promise.$$state);
24056 notify: function(progress) {
24057 var callbacks = this.promise.$$state.pending;
24059 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
24060 nextTick(function() {
24061 var callback, result;
24062 for (var i = 0, ii = callbacks.length; i < ii; i++) {
24063 result = callbacks[i][0];
24064 callback = callbacks[i][3];
24066 result.notify(isFunction(callback) ? callback(progress) : progress);
24068 exceptionHandler(e);
24082 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
24083 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
24084 * a promise chain, you don't need to worry about it.
24086 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
24087 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
24088 * a promise error callback and you want to forward the error to the promise derived from the
24089 * current promise, you have to "rethrow" the error by returning a rejection constructed via
24093 * promiseB = promiseA.then(function(result) {
24094 * // success: do something and resolve promiseB
24095 * // with the old or a new result
24097 * }, function(reason) {
24098 * // error: handle the error if possible and
24099 * // resolve promiseB with newPromiseOrValue,
24100 * // otherwise forward the rejection to promiseB
24101 * if (canHandle(reason)) {
24102 * // handle the error and recover
24103 * return newPromiseOrValue;
24105 * return $q.reject(reason);
24109 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
24110 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
24112 var reject = function(reason) {
24113 var result = new Deferred();
24114 result.reject(reason);
24115 return result.promise;
24118 var makePromise = function makePromise(value, resolved) {
24119 var result = new Deferred();
24121 result.resolve(value);
24123 result.reject(value);
24125 return result.promise;
24128 var handleCallback = function handleCallback(value, isResolved, callback) {
24129 var callbackOutput = null;
24131 if (isFunction(callback)) callbackOutput = callback();
24133 return makePromise(e, false);
24135 if (isPromiseLike(callbackOutput)) {
24136 return callbackOutput.then(function() {
24137 return makePromise(value, isResolved);
24138 }, function(error) {
24139 return makePromise(error, false);
24142 return makePromise(value, isResolved);
24152 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
24153 * This is useful when you are dealing with an object that might or might not be a promise, or if
24154 * the promise comes from a source that can't be trusted.
24156 * @param {*} value Value or a promise
24157 * @param {Function=} successCallback
24158 * @param {Function=} errorCallback
24159 * @param {Function=} progressCallback
24160 * @returns {Promise} Returns a promise of the passed value or promise
24164 var when = function(value, callback, errback, progressBack) {
24165 var result = new Deferred();
24166 result.resolve(value);
24167 return result.promise.then(callback, errback, progressBack);
24176 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
24178 * @param {*} value Value or a promise
24179 * @param {Function=} successCallback
24180 * @param {Function=} errorCallback
24181 * @param {Function=} progressCallback
24182 * @returns {Promise} Returns a promise of the passed value or promise
24184 var resolve = when;
24192 * Combines multiple promises into a single promise that is resolved when all of the input
24193 * promises are resolved.
24195 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
24196 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
24197 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
24198 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
24199 * with the same rejection value.
24202 function all(promises) {
24203 var deferred = new Deferred(),
24205 results = isArray(promises) ? [] : {};
24207 forEach(promises, function(promise, key) {
24209 when(promise).then(function(value) {
24210 if (results.hasOwnProperty(key)) return;
24211 results[key] = value;
24212 if (!(--counter)) deferred.resolve(results);
24213 }, function(reason) {
24214 if (results.hasOwnProperty(key)) return;
24215 deferred.reject(reason);
24219 if (counter === 0) {
24220 deferred.resolve(results);
24223 return deferred.promise;
24226 var $Q = function Q(resolver) {
24227 if (!isFunction(resolver)) {
24228 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
24231 if (!(this instanceof Q)) {
24232 // More useful when $Q is the Promise itself.
24233 return new Q(resolver);
24236 var deferred = new Deferred();
24238 function resolveFn(value) {
24239 deferred.resolve(value);
24242 function rejectFn(reason) {
24243 deferred.reject(reason);
24246 resolver(resolveFn, rejectFn);
24248 return deferred.promise;
24252 $Q.reject = reject;
24254 $Q.resolve = resolve;
24260 function $$RAFProvider() { //rAF
24261 this.$get = ['$window', '$timeout', function($window, $timeout) {
24262 var requestAnimationFrame = $window.requestAnimationFrame ||
24263 $window.webkitRequestAnimationFrame;
24265 var cancelAnimationFrame = $window.cancelAnimationFrame ||
24266 $window.webkitCancelAnimationFrame ||
24267 $window.webkitCancelRequestAnimationFrame;
24269 var rafSupported = !!requestAnimationFrame;
24270 var raf = rafSupported
24272 var id = requestAnimationFrame(fn);
24273 return function() {
24274 cancelAnimationFrame(id);
24278 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
24279 return function() {
24280 $timeout.cancel(timer);
24284 raf.supported = rafSupported;
24293 * The design decisions behind the scope are heavily favored for speed and memory consumption.
24295 * The typical use of scope is to watch the expressions, which most of the time return the same
24296 * value as last time so we optimize the operation.
24298 * Closures construction is expensive in terms of speed as well as memory:
24299 * - No closures, instead use prototypical inheritance for API
24300 * - Internal state needs to be stored on scope directly, which means that private state is
24301 * exposed as $$____ properties
24303 * Loop operations are optimized by using while(count--) { ... }
24304 * - This means that in order to keep the same order of execution as addition we have to add
24305 * items to the array at the beginning (unshift) instead of at the end (push)
24307 * Child scopes are created and removed often
24308 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
24310 * There are fewer watches than observers. This is why you don't want the observer to be implemented
24311 * in the same way as watch. Watch requires return of the initialization function which is expensive
24318 * @name $rootScopeProvider
24321 * Provider for the $rootScope service.
24326 * @name $rootScopeProvider#digestTtl
24329 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
24330 * assuming that the model is unstable.
24332 * The current default is 10 iterations.
24334 * In complex applications it's possible that the dependencies between `$watch`s will result in
24335 * several digest iterations. However if an application needs more than the default 10 digest
24336 * iterations for its model to stabilize then you should investigate what is causing the model to
24337 * continuously change during the digest.
24339 * Increasing the TTL could have performance implications, so you should not change it without
24340 * proper justification.
24342 * @param {number} limit The number of digest iterations.
24351 * Every application has a single root {@link ng.$rootScope.Scope scope}.
24352 * All other scopes are descendant scopes of the root scope. Scopes provide separation
24353 * between the model and the view, via a mechanism for watching the model for changes.
24354 * They also provide event emission/broadcast and subscription facility. See the
24355 * {@link guide/scope developer guide on scopes}.
24357 function $RootScopeProvider() {
24359 var $rootScopeMinErr = minErr('$rootScope');
24360 var lastDirtyWatch = null;
24361 var applyAsyncId = null;
24363 this.digestTtl = function(value) {
24364 if (arguments.length) {
24370 function createChildScopeClass(parent) {
24371 function ChildScope() {
24372 this.$$watchers = this.$$nextSibling =
24373 this.$$childHead = this.$$childTail = null;
24374 this.$$listeners = {};
24375 this.$$listenerCount = {};
24376 this.$$watchersCount = 0;
24377 this.$id = nextUid();
24378 this.$$ChildScope = null;
24380 ChildScope.prototype = parent;
24384 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
24385 function($injector, $exceptionHandler, $parse, $browser) {
24387 function destroyChildScope($event) {
24388 $event.currentScope.$$destroyed = true;
24391 function cleanUpScope($scope) {
24394 // There is a memory leak in IE9 if all child scopes are not disconnected
24395 // completely when a scope is destroyed. So this code will recurse up through
24396 // all this scopes children
24398 // See issue https://github.com/angular/angular.js/issues/10706
24399 $scope.$$childHead && cleanUpScope($scope.$$childHead);
24400 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
24403 // The code below works around IE9 and V8's memory leaks
24406 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
24407 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
24408 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
24410 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
24411 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
24416 * @name $rootScope.Scope
24419 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
24420 * {@link auto.$injector $injector}. Child scopes are created using the
24421 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
24422 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
24423 * an in-depth introduction and usage examples.
24427 * A scope can inherit from a parent scope, as in this example:
24429 var parent = $rootScope;
24430 var child = parent.$new();
24432 parent.salutation = "Hello";
24433 expect(child.salutation).toEqual('Hello');
24435 child.salutation = "Welcome";
24436 expect(child.salutation).toEqual('Welcome');
24437 expect(parent.salutation).toEqual('Hello');
24440 * When interacting with `Scope` in tests, additional helper methods are available on the
24441 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
24445 * @param {Object.<string, function()>=} providers Map of service factory which need to be
24446 * provided for the current scope. Defaults to {@link ng}.
24447 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
24448 * append/override services provided by `providers`. This is handy
24449 * when unit-testing and having the need to override a default
24451 * @returns {Object} Newly created scope.
24455 this.$id = nextUid();
24456 this.$$phase = this.$parent = this.$$watchers =
24457 this.$$nextSibling = this.$$prevSibling =
24458 this.$$childHead = this.$$childTail = null;
24460 this.$$destroyed = false;
24461 this.$$listeners = {};
24462 this.$$listenerCount = {};
24463 this.$$watchersCount = 0;
24464 this.$$isolateBindings = null;
24469 * @name $rootScope.Scope#$id
24472 * Unique scope ID (monotonically increasing) useful for debugging.
24477 * @name $rootScope.Scope#$parent
24480 * Reference to the parent scope.
24485 * @name $rootScope.Scope#$root
24488 * Reference to the root scope.
24491 Scope.prototype = {
24492 constructor: Scope,
24495 * @name $rootScope.Scope#$new
24499 * Creates a new child {@link ng.$rootScope.Scope scope}.
24501 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
24502 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
24504 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
24505 * desired for the scope and its child scopes to be permanently detached from the parent and
24506 * thus stop participating in model change detection and listener notification by invoking.
24508 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
24509 * parent scope. The scope is isolated, as it can not see parent scope properties.
24510 * When creating widgets, it is useful for the widget to not accidentally read parent
24513 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
24514 * of the newly created scope. Defaults to `this` scope if not provided.
24515 * This is used when creating a transclude scope to correctly place it
24516 * in the scope hierarchy while maintaining the correct prototypical
24519 * @returns {Object} The newly created child scope.
24522 $new: function(isolate, parent) {
24525 parent = parent || this;
24528 child = new Scope();
24529 child.$root = this.$root;
24531 // Only create a child scope class if somebody asks for one,
24532 // but cache it to allow the VM to optimize lookups.
24533 if (!this.$$ChildScope) {
24534 this.$$ChildScope = createChildScopeClass(this);
24536 child = new this.$$ChildScope();
24538 child.$parent = parent;
24539 child.$$prevSibling = parent.$$childTail;
24540 if (parent.$$childHead) {
24541 parent.$$childTail.$$nextSibling = child;
24542 parent.$$childTail = child;
24544 parent.$$childHead = parent.$$childTail = child;
24547 // When the new scope is not isolated or we inherit from `this`, and
24548 // the parent scope is destroyed, the property `$$destroyed` is inherited
24549 // prototypically. In all other cases, this property needs to be set
24550 // when the parent scope is destroyed.
24551 // The listener needs to be added after the parent is set
24552 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
24559 * @name $rootScope.Scope#$watch
24563 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
24565 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
24566 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
24567 * its value when executed multiple times with the same input because it may be executed multiple
24568 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
24569 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
24570 * - The `listener` is called only when the value from the current `watchExpression` and the
24571 * previous call to `watchExpression` are not equal (with the exception of the initial run,
24572 * see below). Inequality is determined according to reference inequality,
24573 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
24574 * via the `!==` Javascript operator, unless `objectEquality == true`
24576 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
24577 * according to the {@link angular.equals} function. To save the value of the object for
24578 * later comparison, the {@link angular.copy} function is used. This therefore means that
24579 * watching complex objects will have adverse memory and performance implications.
24580 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
24581 * This is achieved by rerunning the watchers until no changes are detected. The rerun
24582 * iteration limit is 10 to prevent an infinite loop deadlock.
24585 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
24586 * you can register a `watchExpression` function with no `listener`. (Be prepared for
24587 * multiple calls to your `watchExpression` because it will execute multiple times in a
24588 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
24590 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
24591 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
24592 * watcher. In rare cases, this is undesirable because the listener is called when the result
24593 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
24594 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
24595 * listener was called due to initialization.
24601 // let's assume that scope was dependency injected as the $rootScope
24602 var scope = $rootScope;
24603 scope.name = 'misko';
24606 expect(scope.counter).toEqual(0);
24607 scope.$watch('name', function(newValue, oldValue) {
24608 scope.counter = scope.counter + 1;
24610 expect(scope.counter).toEqual(0);
24613 // the listener is always called during the first $digest loop after it was registered
24614 expect(scope.counter).toEqual(1);
24617 // but now it will not be called unless the value changes
24618 expect(scope.counter).toEqual(1);
24620 scope.name = 'adam';
24622 expect(scope.counter).toEqual(2);
24626 // Using a function as a watchExpression
24628 scope.foodCounter = 0;
24629 expect(scope.foodCounter).toEqual(0);
24631 // This function returns the value being watched. It is called for each turn of the $digest loop
24632 function() { return food; },
24633 // This is the change listener, called when the value returned from the above function changes
24634 function(newValue, oldValue) {
24635 if ( newValue !== oldValue ) {
24636 // Only increment the counter if the value changed
24637 scope.foodCounter = scope.foodCounter + 1;
24641 // No digest has been run so the counter will be zero
24642 expect(scope.foodCounter).toEqual(0);
24644 // Run the digest but since food has not changed count will still be zero
24646 expect(scope.foodCounter).toEqual(0);
24648 // Update food and run digest. Now the counter will increment
24649 food = 'cheeseburger';
24651 expect(scope.foodCounter).toEqual(1);
24657 * @param {(function()|string)} watchExpression Expression that is evaluated on each
24658 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
24659 * a call to the `listener`.
24661 * - `string`: Evaluated as {@link guide/expression expression}
24662 * - `function(scope)`: called with current `scope` as a parameter.
24663 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
24664 * of `watchExpression` changes.
24666 * - `newVal` contains the current value of the `watchExpression`
24667 * - `oldVal` contains the previous value of the `watchExpression`
24668 * - `scope` refers to the current scope
24669 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
24670 * comparing for reference equality.
24671 * @returns {function()} Returns a deregistration function for this listener.
24673 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
24674 var get = $parse(watchExp);
24676 if (get.$$watchDelegate) {
24677 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
24680 array = scope.$$watchers,
24683 last: initWatchVal,
24685 exp: prettyPrintExpression || watchExp,
24686 eq: !!objectEquality
24689 lastDirtyWatch = null;
24691 if (!isFunction(listener)) {
24696 array = scope.$$watchers = [];
24698 // we use unshift since we use a while loop in $digest for speed.
24699 // the while loop reads in reverse order.
24700 array.unshift(watcher);
24701 incrementWatchersCount(this, 1);
24703 return function deregisterWatch() {
24704 if (arrayRemove(array, watcher) >= 0) {
24705 incrementWatchersCount(scope, -1);
24707 lastDirtyWatch = null;
24713 * @name $rootScope.Scope#$watchGroup
24717 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
24718 * If any one expression in the collection changes the `listener` is executed.
24720 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
24721 * call to $digest() to see if any items changes.
24722 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
24724 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
24725 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
24727 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
24728 * expression in `watchExpressions` changes
24729 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
24730 * those of `watchExpression`
24731 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
24732 * those of `watchExpression`
24733 * The `scope` refers to the current scope.
24734 * @returns {function()} Returns a de-registration function for all listeners.
24736 $watchGroup: function(watchExpressions, listener) {
24737 var oldValues = new Array(watchExpressions.length);
24738 var newValues = new Array(watchExpressions.length);
24739 var deregisterFns = [];
24741 var changeReactionScheduled = false;
24742 var firstRun = true;
24744 if (!watchExpressions.length) {
24745 // No expressions means we call the listener ASAP
24746 var shouldCall = true;
24747 self.$evalAsync(function() {
24748 if (shouldCall) listener(newValues, newValues, self);
24750 return function deregisterWatchGroup() {
24751 shouldCall = false;
24755 if (watchExpressions.length === 1) {
24756 // Special case size of one
24757 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
24758 newValues[0] = value;
24759 oldValues[0] = oldValue;
24760 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
24764 forEach(watchExpressions, function(expr, i) {
24765 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
24766 newValues[i] = value;
24767 oldValues[i] = oldValue;
24768 if (!changeReactionScheduled) {
24769 changeReactionScheduled = true;
24770 self.$evalAsync(watchGroupAction);
24773 deregisterFns.push(unwatchFn);
24776 function watchGroupAction() {
24777 changeReactionScheduled = false;
24781 listener(newValues, newValues, self);
24783 listener(newValues, oldValues, self);
24787 return function deregisterWatchGroup() {
24788 while (deregisterFns.length) {
24789 deregisterFns.shift()();
24797 * @name $rootScope.Scope#$watchCollection
24801 * Shallow watches the properties of an object and fires whenever any of the properties change
24802 * (for arrays, this implies watching the array items; for object maps, this implies watching
24803 * the properties). If a change is detected, the `listener` callback is fired.
24805 * - The `obj` collection is observed via standard $watch operation and is examined on every
24806 * call to $digest() to see if any items have been added, removed, or moved.
24807 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
24808 * adding, removing, and moving items belonging to an object or array.
24813 $scope.names = ['igor', 'matias', 'misko', 'james'];
24814 $scope.dataCount = 4;
24816 $scope.$watchCollection('names', function(newNames, oldNames) {
24817 $scope.dataCount = newNames.length;
24820 expect($scope.dataCount).toEqual(4);
24823 //still at 4 ... no changes
24824 expect($scope.dataCount).toEqual(4);
24826 $scope.names.pop();
24829 //now there's been a change
24830 expect($scope.dataCount).toEqual(3);
24834 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
24835 * expression value should evaluate to an object or an array which is observed on each
24836 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
24837 * collection will trigger a call to the `listener`.
24839 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
24840 * when a change is detected.
24841 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
24842 * - The `oldCollection` object is a copy of the former collection data.
24843 * Due to performance considerations, the`oldCollection` value is computed only if the
24844 * `listener` function declares two or more arguments.
24845 * - The `scope` argument refers to the current scope.
24847 * @returns {function()} Returns a de-registration function for this listener. When the
24848 * de-registration function is executed, the internal watch operation is terminated.
24850 $watchCollection: function(obj, listener) {
24851 $watchCollectionInterceptor.$stateful = true;
24854 // the current value, updated on each dirty-check run
24856 // a shallow copy of the newValue from the last dirty-check run,
24857 // updated to match newValue during dirty-check run
24859 // a shallow copy of the newValue from when the last change happened
24861 // only track veryOldValue if the listener is asking for it
24862 var trackVeryOldValue = (listener.length > 1);
24863 var changeDetected = 0;
24864 var changeDetector = $parse(obj, $watchCollectionInterceptor);
24865 var internalArray = [];
24866 var internalObject = {};
24867 var initRun = true;
24870 function $watchCollectionInterceptor(_value) {
24872 var newLength, key, bothNaN, newItem, oldItem;
24874 // If the new value is undefined, then return undefined as the watch may be a one-time watch
24875 if (isUndefined(newValue)) return;
24877 if (!isObject(newValue)) { // if primitive
24878 if (oldValue !== newValue) {
24879 oldValue = newValue;
24882 } else if (isArrayLike(newValue)) {
24883 if (oldValue !== internalArray) {
24884 // we are transitioning from something which was not an array into array.
24885 oldValue = internalArray;
24886 oldLength = oldValue.length = 0;
24890 newLength = newValue.length;
24892 if (oldLength !== newLength) {
24893 // if lengths do not match we need to trigger change notification
24895 oldValue.length = oldLength = newLength;
24897 // copy the items to oldValue and look for changes.
24898 for (var i = 0; i < newLength; i++) {
24899 oldItem = oldValue[i];
24900 newItem = newValue[i];
24902 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
24903 if (!bothNaN && (oldItem !== newItem)) {
24905 oldValue[i] = newItem;
24909 if (oldValue !== internalObject) {
24910 // we are transitioning from something which was not an object into object.
24911 oldValue = internalObject = {};
24915 // copy the items to oldValue and look for changes.
24917 for (key in newValue) {
24918 if (hasOwnProperty.call(newValue, key)) {
24920 newItem = newValue[key];
24921 oldItem = oldValue[key];
24923 if (key in oldValue) {
24924 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
24925 if (!bothNaN && (oldItem !== newItem)) {
24927 oldValue[key] = newItem;
24931 oldValue[key] = newItem;
24936 if (oldLength > newLength) {
24937 // we used to have more keys, need to find them and destroy them.
24939 for (key in oldValue) {
24940 if (!hasOwnProperty.call(newValue, key)) {
24942 delete oldValue[key];
24947 return changeDetected;
24950 function $watchCollectionAction() {
24953 listener(newValue, newValue, self);
24955 listener(newValue, veryOldValue, self);
24958 // make a copy for the next time a collection is changed
24959 if (trackVeryOldValue) {
24960 if (!isObject(newValue)) {
24962 veryOldValue = newValue;
24963 } else if (isArrayLike(newValue)) {
24964 veryOldValue = new Array(newValue.length);
24965 for (var i = 0; i < newValue.length; i++) {
24966 veryOldValue[i] = newValue[i];
24968 } else { // if object
24970 for (var key in newValue) {
24971 if (hasOwnProperty.call(newValue, key)) {
24972 veryOldValue[key] = newValue[key];
24979 return this.$watch(changeDetector, $watchCollectionAction);
24984 * @name $rootScope.Scope#$digest
24988 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
24989 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
24990 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
24991 * until no more listeners are firing. This means that it is possible to get into an infinite
24992 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
24993 * iterations exceeds 10.
24995 * Usually, you don't call `$digest()` directly in
24996 * {@link ng.directive:ngController controllers} or in
24997 * {@link ng.$compileProvider#directive directives}.
24998 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
24999 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
25001 * If you want to be notified whenever `$digest()` is called,
25002 * you can register a `watchExpression` function with
25003 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
25005 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
25010 scope.name = 'misko';
25013 expect(scope.counter).toEqual(0);
25014 scope.$watch('name', function(newValue, oldValue) {
25015 scope.counter = scope.counter + 1;
25017 expect(scope.counter).toEqual(0);
25020 // the listener is always called during the first $digest loop after it was registered
25021 expect(scope.counter).toEqual(1);
25024 // but now it will not be called unless the value changes
25025 expect(scope.counter).toEqual(1);
25027 scope.name = 'adam';
25029 expect(scope.counter).toEqual(2);
25033 $digest: function() {
25034 var watch, value, last,
25038 next, current, target = this,
25040 logIdx, logMsg, asyncTask;
25042 beginPhase('$digest');
25043 // Check for changes to browser url that happened in sync before the call to $digest
25044 $browser.$$checkUrlChange();
25046 if (this === $rootScope && applyAsyncId !== null) {
25047 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
25048 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
25049 $browser.defer.cancel(applyAsyncId);
25053 lastDirtyWatch = null;
25055 do { // "while dirty" loop
25059 while (asyncQueue.length) {
25061 asyncTask = asyncQueue.shift();
25062 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
25064 $exceptionHandler(e);
25066 lastDirtyWatch = null;
25069 traverseScopesLoop:
25070 do { // "traverse the scopes" loop
25071 if ((watchers = current.$$watchers)) {
25072 // process our watches
25073 length = watchers.length;
25076 watch = watchers[length];
25077 // Most common watches are on primitives, in which case we can short
25078 // circuit it with === operator, only when === fails do we use .equals
25080 if ((value = watch.get(current)) !== (last = watch.last) &&
25082 ? equals(value, last)
25083 : (typeof value === 'number' && typeof last === 'number'
25084 && isNaN(value) && isNaN(last)))) {
25086 lastDirtyWatch = watch;
25087 watch.last = watch.eq ? copy(value, null) : value;
25088 watch.fn(value, ((last === initWatchVal) ? value : last), current);
25091 if (!watchLog[logIdx]) watchLog[logIdx] = [];
25092 watchLog[logIdx].push({
25093 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
25098 } else if (watch === lastDirtyWatch) {
25099 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
25100 // have already been tested.
25102 break traverseScopesLoop;
25106 $exceptionHandler(e);
25111 // Insanity Warning: scope depth-first traversal
25112 // yes, this code is a bit crazy, but it works and we have tests to prove it!
25113 // this piece should be kept in sync with the traversal in $broadcast
25114 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
25115 (current !== target && current.$$nextSibling)))) {
25116 while (current !== target && !(next = current.$$nextSibling)) {
25117 current = current.$parent;
25120 } while ((current = next));
25122 // `break traverseScopesLoop;` takes us to here
25124 if ((dirty || asyncQueue.length) && !(ttl--)) {
25126 throw $rootScopeMinErr('infdig',
25127 '{0} $digest() iterations reached. Aborting!\n' +
25128 'Watchers fired in the last 5 iterations: {1}',
25132 } while (dirty || asyncQueue.length);
25136 while (postDigestQueue.length) {
25138 postDigestQueue.shift()();
25140 $exceptionHandler(e);
25148 * @name $rootScope.Scope#$destroy
25149 * @eventType broadcast on scope being destroyed
25152 * Broadcasted when a scope and its children are being destroyed.
25154 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
25155 * clean up DOM bindings before an element is removed from the DOM.
25160 * @name $rootScope.Scope#$destroy
25164 * Removes the current scope (and all of its children) from the parent scope. Removal implies
25165 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
25166 * propagate to the current scope and its children. Removal also implies that the current
25167 * scope is eligible for garbage collection.
25169 * The `$destroy()` is usually used by directives such as
25170 * {@link ng.directive:ngRepeat ngRepeat} for managing the
25171 * unrolling of the loop.
25173 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
25174 * Application code can register a `$destroy` event handler that will give it a chance to
25175 * perform any necessary cleanup.
25177 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
25178 * clean up DOM bindings before an element is removed from the DOM.
25180 $destroy: function() {
25181 // We can't destroy a scope that has been already destroyed.
25182 if (this.$$destroyed) return;
25183 var parent = this.$parent;
25185 this.$broadcast('$destroy');
25186 this.$$destroyed = true;
25188 if (this === $rootScope) {
25189 //Remove handlers attached to window when $rootScope is removed
25190 $browser.$$applicationDestroyed();
25193 incrementWatchersCount(this, -this.$$watchersCount);
25194 for (var eventName in this.$$listenerCount) {
25195 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
25198 // sever all the references to parent scopes (after this cleanup, the current scope should
25199 // not be retained by any of our references and should be eligible for garbage collection)
25200 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
25201 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
25202 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
25203 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
25205 // Disable listeners, watchers and apply/digest methods
25206 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
25207 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
25208 this.$$listeners = {};
25210 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
25211 this.$$nextSibling = null;
25212 cleanUpScope(this);
25217 * @name $rootScope.Scope#$eval
25221 * Executes the `expression` on the current scope and returns the result. Any exceptions in
25222 * the expression are propagated (uncaught). This is useful when evaluating Angular
25227 var scope = ng.$rootScope.Scope();
25231 expect(scope.$eval('a+b')).toEqual(3);
25232 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
25235 * @param {(string|function())=} expression An angular expression to be executed.
25237 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
25238 * - `function(scope)`: execute the function with the current `scope` parameter.
25240 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
25241 * @returns {*} The result of evaluating the expression.
25243 $eval: function(expr, locals) {
25244 return $parse(expr)(this, locals);
25249 * @name $rootScope.Scope#$evalAsync
25253 * Executes the expression on the current scope at a later point in time.
25255 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
25258 * - it will execute after the function that scheduled the evaluation (preferably before DOM
25260 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
25261 * `expression` execution.
25263 * Any exceptions from the execution of the expression are forwarded to the
25264 * {@link ng.$exceptionHandler $exceptionHandler} service.
25266 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
25267 * will be scheduled. However, it is encouraged to always call code that changes the model
25268 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
25270 * @param {(string|function())=} expression An angular expression to be executed.
25272 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
25273 * - `function(scope)`: execute the function with the current `scope` parameter.
25275 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
25277 $evalAsync: function(expr, locals) {
25278 // if we are outside of an $digest loop and this is the first time we are scheduling async
25279 // task also schedule async auto-flush
25280 if (!$rootScope.$$phase && !asyncQueue.length) {
25281 $browser.defer(function() {
25282 if (asyncQueue.length) {
25283 $rootScope.$digest();
25288 asyncQueue.push({scope: this, expression: expr, locals: locals});
25291 $$postDigest: function(fn) {
25292 postDigestQueue.push(fn);
25297 * @name $rootScope.Scope#$apply
25301 * `$apply()` is used to execute an expression in angular from outside of the angular
25302 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
25303 * Because we are calling into the angular framework we need to perform proper scope life
25304 * cycle of {@link ng.$exceptionHandler exception handling},
25305 * {@link ng.$rootScope.Scope#$digest executing watches}.
25309 * # Pseudo-Code of `$apply()`
25311 function $apply(expr) {
25313 return $eval(expr);
25315 $exceptionHandler(e);
25323 * Scope's `$apply()` method transitions through the following stages:
25325 * 1. The {@link guide/expression expression} is executed using the
25326 * {@link ng.$rootScope.Scope#$eval $eval()} method.
25327 * 2. Any exceptions from the execution of the expression are forwarded to the
25328 * {@link ng.$exceptionHandler $exceptionHandler} service.
25329 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
25330 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
25333 * @param {(string|function())=} exp An angular expression to be executed.
25335 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
25336 * - `function(scope)`: execute the function with current `scope` parameter.
25338 * @returns {*} The result of evaluating the expression.
25340 $apply: function(expr) {
25342 beginPhase('$apply');
25344 return this.$eval(expr);
25349 $exceptionHandler(e);
25352 $rootScope.$digest();
25354 $exceptionHandler(e);
25362 * @name $rootScope.Scope#$applyAsync
25366 * Schedule the invocation of $apply to occur at a later time. The actual time difference
25367 * varies across browsers, but is typically around ~10 milliseconds.
25369 * This can be used to queue up multiple expressions which need to be evaluated in the same
25372 * @param {(string|function())=} exp An angular expression to be executed.
25374 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
25375 * - `function(scope)`: execute the function with current `scope` parameter.
25377 $applyAsync: function(expr) {
25379 expr && applyAsyncQueue.push($applyAsyncExpression);
25380 scheduleApplyAsync();
25382 function $applyAsyncExpression() {
25389 * @name $rootScope.Scope#$on
25393 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
25394 * discussion of event life cycle.
25396 * The event listener function format is: `function(event, args...)`. The `event` object
25397 * passed into the listener has the following attributes:
25399 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
25401 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
25402 * event propagates through the scope hierarchy, this property is set to null.
25403 * - `name` - `{string}`: name of the event.
25404 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
25405 * further event propagation (available only for events that were `$emit`-ed).
25406 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
25408 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
25410 * @param {string} name Event name to listen on.
25411 * @param {function(event, ...args)} listener Function to call when the event is emitted.
25412 * @returns {function()} Returns a deregistration function for this listener.
25414 $on: function(name, listener) {
25415 var namedListeners = this.$$listeners[name];
25416 if (!namedListeners) {
25417 this.$$listeners[name] = namedListeners = [];
25419 namedListeners.push(listener);
25421 var current = this;
25423 if (!current.$$listenerCount[name]) {
25424 current.$$listenerCount[name] = 0;
25426 current.$$listenerCount[name]++;
25427 } while ((current = current.$parent));
25430 return function() {
25431 var indexOfListener = namedListeners.indexOf(listener);
25432 if (indexOfListener !== -1) {
25433 namedListeners[indexOfListener] = null;
25434 decrementListenerCount(self, 1, name);
25442 * @name $rootScope.Scope#$emit
25446 * Dispatches an event `name` upwards through the scope hierarchy notifying the
25447 * registered {@link ng.$rootScope.Scope#$on} listeners.
25449 * The event life cycle starts at the scope on which `$emit` was called. All
25450 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
25451 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
25452 * registered listeners along the way. The event will stop propagating if one of the listeners
25455 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
25456 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
25458 * @param {string} name Event name to emit.
25459 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
25460 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
25462 $emit: function(name, args) {
25466 stopPropagation = false,
25469 targetScope: scope,
25470 stopPropagation: function() {stopPropagation = true;},
25471 preventDefault: function() {
25472 event.defaultPrevented = true;
25474 defaultPrevented: false
25476 listenerArgs = concat([event], arguments, 1),
25480 namedListeners = scope.$$listeners[name] || empty;
25481 event.currentScope = scope;
25482 for (i = 0, length = namedListeners.length; i < length; i++) {
25484 // if listeners were deregistered, defragment the array
25485 if (!namedListeners[i]) {
25486 namedListeners.splice(i, 1);
25492 //allow all listeners attached to the current scope to run
25493 namedListeners[i].apply(null, listenerArgs);
25495 $exceptionHandler(e);
25498 //if any listener on the current scope stops propagation, prevent bubbling
25499 if (stopPropagation) {
25500 event.currentScope = null;
25504 scope = scope.$parent;
25507 event.currentScope = null;
25515 * @name $rootScope.Scope#$broadcast
25519 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
25520 * registered {@link ng.$rootScope.Scope#$on} listeners.
25522 * The event life cycle starts at the scope on which `$broadcast` was called. All
25523 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
25524 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
25525 * scope and calls all registered listeners along the way. The event cannot be canceled.
25527 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
25528 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
25530 * @param {string} name Event name to broadcast.
25531 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
25532 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
25534 $broadcast: function(name, args) {
25540 targetScope: target,
25541 preventDefault: function() {
25542 event.defaultPrevented = true;
25544 defaultPrevented: false
25547 if (!target.$$listenerCount[name]) return event;
25549 var listenerArgs = concat([event], arguments, 1),
25550 listeners, i, length;
25552 //down while you can, then up and next sibling or up and next sibling until back at root
25553 while ((current = next)) {
25554 event.currentScope = current;
25555 listeners = current.$$listeners[name] || [];
25556 for (i = 0, length = listeners.length; i < length; i++) {
25557 // if listeners were deregistered, defragment the array
25558 if (!listeners[i]) {
25559 listeners.splice(i, 1);
25566 listeners[i].apply(null, listenerArgs);
25568 $exceptionHandler(e);
25572 // Insanity Warning: scope depth-first traversal
25573 // yes, this code is a bit crazy, but it works and we have tests to prove it!
25574 // this piece should be kept in sync with the traversal in $digest
25575 // (though it differs due to having the extra check for $$listenerCount)
25576 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
25577 (current !== target && current.$$nextSibling)))) {
25578 while (current !== target && !(next = current.$$nextSibling)) {
25579 current = current.$parent;
25584 event.currentScope = null;
25589 var $rootScope = new Scope();
25591 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
25592 var asyncQueue = $rootScope.$$asyncQueue = [];
25593 var postDigestQueue = $rootScope.$$postDigestQueue = [];
25594 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
25599 function beginPhase(phase) {
25600 if ($rootScope.$$phase) {
25601 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
25604 $rootScope.$$phase = phase;
25607 function clearPhase() {
25608 $rootScope.$$phase = null;
25611 function incrementWatchersCount(current, count) {
25613 current.$$watchersCount += count;
25614 } while ((current = current.$parent));
25617 function decrementListenerCount(current, count, name) {
25619 current.$$listenerCount[name] -= count;
25621 if (current.$$listenerCount[name] === 0) {
25622 delete current.$$listenerCount[name];
25624 } while ((current = current.$parent));
25628 * function used as an initial value for watchers.
25629 * because it's unique we can easily tell it apart from other values
25631 function initWatchVal() {}
25633 function flushApplyAsync() {
25634 while (applyAsyncQueue.length) {
25636 applyAsyncQueue.shift()();
25638 $exceptionHandler(e);
25641 applyAsyncId = null;
25644 function scheduleApplyAsync() {
25645 if (applyAsyncId === null) {
25646 applyAsyncId = $browser.defer(function() {
25647 $rootScope.$apply(flushApplyAsync);
25656 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
25658 function $$SanitizeUriProvider() {
25659 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
25660 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
25664 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
25665 * urls during a[href] sanitization.
25667 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
25669 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
25670 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
25671 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
25672 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
25674 * @param {RegExp=} regexp New regexp to whitelist urls with.
25675 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
25676 * chaining otherwise.
25678 this.aHrefSanitizationWhitelist = function(regexp) {
25679 if (isDefined(regexp)) {
25680 aHrefSanitizationWhitelist = regexp;
25683 return aHrefSanitizationWhitelist;
25689 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
25690 * urls during img[src] sanitization.
25692 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
25694 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
25695 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
25696 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
25697 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
25699 * @param {RegExp=} regexp New regexp to whitelist urls with.
25700 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
25701 * chaining otherwise.
25703 this.imgSrcSanitizationWhitelist = function(regexp) {
25704 if (isDefined(regexp)) {
25705 imgSrcSanitizationWhitelist = regexp;
25708 return imgSrcSanitizationWhitelist;
25711 this.$get = function() {
25712 return function sanitizeUri(uri, isImage) {
25713 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
25715 normalizedVal = urlResolve(uri).href;
25716 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
25717 return 'unsafe:' + normalizedVal;
25724 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25725 * Any commits to this file should be reviewed with security in mind. *
25726 * Changes to this file can potentially create security vulnerabilities. *
25727 * An approval from 2 Core members with history of modifying *
25728 * this file is required. *
25730 * Does the change somehow allow for arbitrary javascript to be executed? *
25731 * Or allows for someone to change the prototype of built-in objects? *
25732 * Or gives undesired access to variables likes document or window? *
25733 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
25735 var $sceMinErr = minErr('$sce');
25737 var SCE_CONTEXTS = {
25741 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
25742 // url. (e.g. ng-include, script src, templateUrl)
25743 RESOURCE_URL: 'resourceUrl',
25747 // Helper functions follow.
25749 function adjustMatcher(matcher) {
25750 if (matcher === 'self') {
25752 } else if (isString(matcher)) {
25753 // Strings match exactly except for 2 wildcards - '*' and '**'.
25754 // '*' matches any character except those from the set ':/.?&'.
25755 // '**' matches any character (like .* in a RegExp).
25756 // More than 2 *'s raises an error as it's ill defined.
25757 if (matcher.indexOf('***') > -1) {
25758 throw $sceMinErr('iwcard',
25759 'Illegal sequence *** in string matcher. String: {0}', matcher);
25761 matcher = escapeForRegexp(matcher).
25762 replace('\\*\\*', '.*').
25763 replace('\\*', '[^:/.?&;]*');
25764 return new RegExp('^' + matcher + '$');
25765 } else if (isRegExp(matcher)) {
25766 // The only other type of matcher allowed is a Regexp.
25767 // Match entire URL / disallow partial matches.
25768 // Flags are reset (i.e. no global, ignoreCase or multiline)
25769 return new RegExp('^' + matcher.source + '$');
25771 throw $sceMinErr('imatcher',
25772 'Matchers may only be "self", string patterns or RegExp objects');
25777 function adjustMatchers(matchers) {
25778 var adjustedMatchers = [];
25779 if (isDefined(matchers)) {
25780 forEach(matchers, function(matcher) {
25781 adjustedMatchers.push(adjustMatcher(matcher));
25784 return adjustedMatchers;
25790 * @name $sceDelegate
25795 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
25796 * Contextual Escaping (SCE)} services to AngularJS.
25798 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
25799 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
25800 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
25801 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
25802 * work because `$sce` delegates to `$sceDelegate` for these operations.
25804 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
25806 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
25807 * can override it completely to change the behavior of `$sce`, the common case would
25808 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
25809 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
25810 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
25811 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
25812 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
25817 * @name $sceDelegateProvider
25820 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
25821 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
25822 * that the URLs used for sourcing Angular templates are safe. Refer {@link
25823 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
25824 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
25826 * For the general details about this service in Angular, read the main page for {@link ng.$sce
25827 * Strict Contextual Escaping (SCE)}.
25829 * **Example**: Consider the following case. <a name="example"></a>
25831 * - your app is hosted at url `http://myapp.example.com/`
25832 * - but some of your templates are hosted on other domains you control such as
25833 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
25834 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
25836 * Here is what a secure configuration for this scenario might look like:
25839 * angular.module('myApp', []).config(function($sceDelegateProvider) {
25840 * $sceDelegateProvider.resourceUrlWhitelist([
25841 * // Allow same origin resource loads.
25843 * // Allow loading from our assets domain. Notice the difference between * and **.
25844 * 'http://srv*.assets.example.com/**'
25847 * // The blacklist overrides the whitelist so the open redirect here is blocked.
25848 * $sceDelegateProvider.resourceUrlBlacklist([
25849 * 'http://myapp.example.com/clickThru**'
25855 function $SceDelegateProvider() {
25856 this.SCE_CONTEXTS = SCE_CONTEXTS;
25858 // Resource URLs can also be trusted by policy.
25859 var resourceUrlWhitelist = ['self'],
25860 resourceUrlBlacklist = [];
25864 * @name $sceDelegateProvider#resourceUrlWhitelist
25867 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
25868 * provided. This must be an array or null. A snapshot of this array is used so further
25869 * changes to the array are ignored.
25871 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
25872 * allowed in this array.
25874 * Note: **an empty whitelist array will block all URLs**!
25876 * @return {Array} the currently set whitelist array.
25878 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
25879 * same origin resource requests.
25882 * Sets/Gets the whitelist of trusted resource URLs.
25884 this.resourceUrlWhitelist = function(value) {
25885 if (arguments.length) {
25886 resourceUrlWhitelist = adjustMatchers(value);
25888 return resourceUrlWhitelist;
25893 * @name $sceDelegateProvider#resourceUrlBlacklist
25896 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
25897 * provided. This must be an array or null. A snapshot of this array is used so further
25898 * changes to the array are ignored.
25900 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
25901 * allowed in this array.
25903 * The typical usage for the blacklist is to **block
25904 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
25905 * these would otherwise be trusted but actually return content from the redirected domain.
25907 * Finally, **the blacklist overrides the whitelist** and has the final say.
25909 * @return {Array} the currently set blacklist array.
25911 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
25912 * is no blacklist.)
25915 * Sets/Gets the blacklist of trusted resource URLs.
25918 this.resourceUrlBlacklist = function(value) {
25919 if (arguments.length) {
25920 resourceUrlBlacklist = adjustMatchers(value);
25922 return resourceUrlBlacklist;
25925 this.$get = ['$injector', function($injector) {
25927 var htmlSanitizer = function htmlSanitizer(html) {
25928 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
25931 if ($injector.has('$sanitize')) {
25932 htmlSanitizer = $injector.get('$sanitize');
25936 function matchUrl(matcher, parsedUrl) {
25937 if (matcher === 'self') {
25938 return urlIsSameOrigin(parsedUrl);
25940 // definitely a regex. See adjustMatchers()
25941 return !!matcher.exec(parsedUrl.href);
25945 function isResourceUrlAllowedByPolicy(url) {
25946 var parsedUrl = urlResolve(url.toString());
25947 var i, n, allowed = false;
25948 // Ensure that at least one item from the whitelist allows this url.
25949 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
25950 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
25956 // Ensure that no item from the blacklist blocked this url.
25957 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
25958 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
25967 function generateHolderType(Base) {
25968 var holderType = function TrustedValueHolderType(trustedValue) {
25969 this.$$unwrapTrustedValue = function() {
25970 return trustedValue;
25974 holderType.prototype = new Base();
25976 holderType.prototype.valueOf = function sceValueOf() {
25977 return this.$$unwrapTrustedValue();
25979 holderType.prototype.toString = function sceToString() {
25980 return this.$$unwrapTrustedValue().toString();
25985 var trustedValueHolderBase = generateHolderType(),
25988 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
25989 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
25990 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
25991 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
25992 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
25996 * @name $sceDelegate#trustAs
25999 * Returns an object that is trusted by angular for use in specified strict
26000 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
26001 * attribute interpolation, any dom event binding attribute interpolation
26002 * such as for onclick, etc.) that uses the provided value.
26003 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
26005 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
26006 * resourceUrl, html, js and css.
26007 * @param {*} value The value that that should be considered trusted/safe.
26008 * @returns {*} A value that can be used to stand in for the provided `value` in places
26009 * where Angular expects a $sce.trustAs() return value.
26011 function trustAs(type, trustedValue) {
26012 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
26013 if (!Constructor) {
26014 throw $sceMinErr('icontext',
26015 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
26016 type, trustedValue);
26018 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
26019 return trustedValue;
26021 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
26022 // mutable objects, we ensure here that the value passed in is actually a string.
26023 if (typeof trustedValue !== 'string') {
26024 throw $sceMinErr('itype',
26025 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
26028 return new Constructor(trustedValue);
26033 * @name $sceDelegate#valueOf
26036 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
26037 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
26038 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
26040 * If the passed parameter is not a value that had been returned by {@link
26041 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
26043 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
26044 * call or anything else.
26045 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
26046 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
26047 * `value` unchanged.
26049 function valueOf(maybeTrusted) {
26050 if (maybeTrusted instanceof trustedValueHolderBase) {
26051 return maybeTrusted.$$unwrapTrustedValue();
26053 return maybeTrusted;
26059 * @name $sceDelegate#getTrusted
26062 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
26063 * returns the originally supplied value if the queried context type is a supertype of the
26064 * created type. If this condition isn't satisfied, throws an exception.
26066 * @param {string} type The kind of context in which this value is to be used.
26067 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
26068 * `$sceDelegate.trustAs`} call.
26069 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
26070 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
26072 function getTrusted(type, maybeTrusted) {
26073 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
26074 return maybeTrusted;
26076 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
26077 if (constructor && maybeTrusted instanceof constructor) {
26078 return maybeTrusted.$$unwrapTrustedValue();
26080 // If we get here, then we may only take one of two actions.
26081 // 1. sanitize the value for the requested type, or
26082 // 2. throw an exception.
26083 if (type === SCE_CONTEXTS.RESOURCE_URL) {
26084 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
26085 return maybeTrusted;
26087 throw $sceMinErr('insecurl',
26088 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
26089 maybeTrusted.toString());
26091 } else if (type === SCE_CONTEXTS.HTML) {
26092 return htmlSanitizer(maybeTrusted);
26094 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
26097 return { trustAs: trustAs,
26098 getTrusted: getTrusted,
26099 valueOf: valueOf };
26106 * @name $sceProvider
26109 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
26110 * - enable/disable Strict Contextual Escaping (SCE) in a module
26111 * - override the default implementation with a custom delegate
26113 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
26116 /* jshint maxlen: false*/
26125 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
26127 * # Strict Contextual Escaping
26129 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
26130 * contexts to result in a value that is marked as safe to use for that context. One example of
26131 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
26132 * to these contexts as privileged or SCE contexts.
26134 * As of version 1.2, Angular ships with SCE enabled by default.
26136 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
26137 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
26138 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
26139 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
26140 * to the top of your HTML document.
26142 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
26143 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
26145 * Here's an example of a binding in a privileged context:
26148 * <input ng-model="userHtml" aria-label="User input">
26149 * <div ng-bind-html="userHtml"></div>
26152 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
26153 * disabled, this application allows the user to render arbitrary HTML into the DIV.
26154 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
26155 * bindings. (HTML is just one example of a context where rendering user controlled input creates
26156 * security vulnerabilities.)
26158 * For the case of HTML, you might use a library, either on the client side, or on the server side,
26159 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
26161 * How would you ensure that every place that used these types of bindings was bound to a value that
26162 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
26163 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
26164 * properties/fields and forgot to update the binding to the sanitized value?
26166 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
26167 * determine that something explicitly says it's safe to use a value for binding in that
26168 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
26169 * for those values that you can easily tell are safe - because they were received from your server,
26170 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
26171 * allowing only the files in a specific directory to do this. Ensuring that the internal API
26172 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
26174 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
26175 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
26176 * obtain values that will be accepted by SCE / privileged contexts.
26179 * ## How does it work?
26181 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
26182 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
26183 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
26184 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
26186 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
26187 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
26191 * var ngBindHtmlDirective = ['$sce', function($sce) {
26192 * return function(scope, element, attr) {
26193 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
26194 * element.html(value || '');
26200 * ## Impact on loading templates
26202 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
26203 * `templateUrl`'s specified by {@link guide/directive directives}.
26205 * By default, Angular only loads templates from the same domain and protocol as the application
26206 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
26207 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
26208 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
26209 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
26213 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
26214 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
26215 * policy apply in addition to this and may further restrict whether the template is successfully
26216 * loaded. This means that without the right CORS policy, loading templates from a different domain
26217 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
26220 * ## This feels like too much overhead
26222 * It's important to remember that SCE only applies to interpolation expressions.
26224 * If your expressions are constant literals, they're automatically trusted and you don't need to
26225 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
26226 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
26228 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
26229 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
26231 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
26232 * templates in `ng-include` from your application's domain without having to even know about SCE.
26233 * It blocks loading templates from other domains or loading templates over http from an https
26234 * served document. You can change these by setting your own custom {@link
26235 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
26236 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
26238 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
26239 * application that's secure and can be audited to verify that with much more ease than bolting
26240 * security onto an application later.
26242 * <a name="contexts"></a>
26243 * ## What trusted context types are supported?
26245 * | Context | Notes |
26246 * |---------------------|----------------|
26247 * | `$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. |
26248 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
26249 * | `$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. |
26250 * | `$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. |
26251 * | `$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. |
26253 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
26255 * Each element in these arrays must be one of the following:
26258 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
26259 * domain** as the application document using the **same protocol**.
26260 * - **String** (except the special value `'self'`)
26261 * - The string is matched against the full *normalized / absolute URL* of the resource
26262 * being tested (substring matches are not good enough.)
26263 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
26264 * match themselves.
26265 * - `*`: matches zero or more occurrences of any character other than one of the following 6
26266 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
26268 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
26269 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
26270 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
26271 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
26272 * http://foo.example.com/templates/**).
26273 * - **RegExp** (*see caveat below*)
26274 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
26275 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
26276 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
26277 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
26278 * small number of cases. A `.` character in the regex used when matching the scheme or a
26279 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
26280 * is highly recommended to use the string patterns and only fall back to regular expressions
26281 * as a last resort.
26282 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
26283 * matched against the **entire** *normalized / absolute URL* of the resource being tested
26284 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
26285 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
26286 * - If you are generating your JavaScript from some other templating engine (not
26287 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
26288 * remember to escape your regular expression (and be aware that you might need more than
26289 * one level of escaping depending on your templating engine and the way you interpolated
26290 * the value.) Do make use of your platform's escaping mechanism as it might be good
26291 * enough before coding your own. E.g. Ruby has
26292 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
26293 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
26294 * Javascript lacks a similar built in function for escaping. Take a look at Google
26295 * Closure library's [goog.string.regExpEscape(s)](
26296 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
26298 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
26300 * ## Show me an example using SCE.
26302 * <example module="mySceApp" deps="angular-sanitize.js">
26303 * <file name="index.html">
26304 * <div ng-controller="AppController as myCtrl">
26305 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
26306 * <b>User comments</b><br>
26307 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
26308 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
26310 * <div class="well">
26311 * <div ng-repeat="userComment in myCtrl.userComments">
26312 * <b>{{userComment.name}}</b>:
26313 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
26320 * <file name="script.js">
26321 * angular.module('mySceApp', ['ngSanitize'])
26322 * .controller('AppController', ['$http', '$templateCache', '$sce',
26323 * function($http, $templateCache, $sce) {
26325 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
26326 * self.userComments = userComments;
26328 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
26329 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
26330 * 'sanitization."">Hover over this text.</span>');
26334 * <file name="test_data.json">
26336 * { "name": "Alice",
26338 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
26341 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
26346 * <file name="protractor.js" type="protractor">
26347 * describe('SCE doc demo', function() {
26348 * it('should sanitize untrusted values', function() {
26349 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
26350 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
26353 * it('should NOT sanitize explicitly trusted values', function() {
26354 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
26355 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
26356 * 'sanitization."">Hover over this text.</span>');
26364 * ## Can I disable SCE completely?
26366 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
26367 * for little coding overhead. It will be much harder to take an SCE disabled application and
26368 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
26369 * for cases where you have a lot of existing code that was written before SCE was introduced and
26370 * you're migrating them a module at a time.
26372 * That said, here's how you can completely disable SCE:
26375 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
26376 * // Completely disable SCE. For demonstration purposes only!
26377 * // Do not use in new projects.
26378 * $sceProvider.enabled(false);
26383 /* jshint maxlen: 100 */
26385 function $SceProvider() {
26386 var enabled = true;
26390 * @name $sceProvider#enabled
26393 * @param {boolean=} value If provided, then enables/disables SCE.
26394 * @return {boolean} true if SCE is enabled, false otherwise.
26397 * Enables/disables SCE and returns the current value.
26399 this.enabled = function(value) {
26400 if (arguments.length) {
26407 /* Design notes on the default implementation for SCE.
26409 * The API contract for the SCE delegate
26410 * -------------------------------------
26411 * The SCE delegate object must provide the following 3 methods:
26413 * - trustAs(contextEnum, value)
26414 * This method is used to tell the SCE service that the provided value is OK to use in the
26415 * contexts specified by contextEnum. It must return an object that will be accepted by
26416 * getTrusted() for a compatible contextEnum and return this value.
26419 * For values that were not produced by trustAs(), return them as is. For values that were
26420 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
26421 * trustAs is wrapping the given values into some type, this operation unwraps it when given
26424 * - getTrusted(contextEnum, value)
26425 * This function should return the a value that is safe to use in the context specified by
26426 * contextEnum or throw and exception otherwise.
26428 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
26429 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
26430 * instance, an implementation could maintain a registry of all trusted objects by context. In
26431 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
26432 * return the same object passed in if it was found in the registry under a compatible context or
26433 * throw an exception otherwise. An implementation might only wrap values some of the time based
26434 * on some criteria. getTrusted() might return a value and not throw an exception for special
26435 * constants or objects even if not wrapped. All such implementations fulfill this contract.
26438 * A note on the inheritance model for SCE contexts
26439 * ------------------------------------------------
26440 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
26441 * is purely an implementation details.
26443 * The contract is simply this:
26445 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
26446 * will also succeed.
26448 * Inheritance happens to capture this in a natural way. In some future, we
26449 * may not use inheritance anymore. That is OK because no code outside of
26450 * sce.js and sceSpecs.js would need to be aware of this detail.
26453 this.$get = ['$parse', '$sceDelegate', function(
26454 $parse, $sceDelegate) {
26455 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
26456 // the "expression(javascript expression)" syntax which is insecure.
26457 if (enabled && msie < 8) {
26458 throw $sceMinErr('iequirks',
26459 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
26460 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
26461 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
26464 var sce = shallowCopy(SCE_CONTEXTS);
26468 * @name $sce#isEnabled
26471 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
26472 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
26475 * Returns a boolean indicating if SCE is enabled.
26477 sce.isEnabled = function() {
26480 sce.trustAs = $sceDelegate.trustAs;
26481 sce.getTrusted = $sceDelegate.getTrusted;
26482 sce.valueOf = $sceDelegate.valueOf;
26485 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
26486 sce.valueOf = identity;
26491 * @name $sce#parseAs
26494 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
26495 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
26496 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
26499 * @param {string} type The kind of SCE context in which this result will be used.
26500 * @param {string} expression String expression to compile.
26501 * @returns {function(context, locals)} a function which represents the compiled expression:
26503 * * `context` – `{object}` – an object against which any expressions embedded in the strings
26504 * are evaluated against (typically a scope object).
26505 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
26508 sce.parseAs = function sceParseAs(type, expr) {
26509 var parsed = $parse(expr);
26510 if (parsed.literal && parsed.constant) {
26513 return $parse(expr, function(value) {
26514 return sce.getTrusted(type, value);
26521 * @name $sce#trustAs
26524 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
26525 * returns an object that is trusted by angular for use in specified strict contextual
26526 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
26527 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
26528 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
26531 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
26532 * resourceUrl, html, js and css.
26533 * @param {*} value The value that that should be considered trusted/safe.
26534 * @returns {*} A value that can be used to stand in for the provided `value` in places
26535 * where Angular expects a $sce.trustAs() return value.
26540 * @name $sce#trustAsHtml
26543 * Shorthand method. `$sce.trustAsHtml(value)` →
26544 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
26546 * @param {*} value The value to trustAs.
26547 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
26548 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
26549 * only accept expressions that are either literal constants or are the
26550 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
26555 * @name $sce#trustAsUrl
26558 * Shorthand method. `$sce.trustAsUrl(value)` →
26559 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
26561 * @param {*} value The value to trustAs.
26562 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
26563 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
26564 * only accept expressions that are either literal constants or are the
26565 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
26570 * @name $sce#trustAsResourceUrl
26573 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
26574 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
26576 * @param {*} value The value to trustAs.
26577 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
26578 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
26579 * only accept expressions that are either literal constants or are the return
26580 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
26585 * @name $sce#trustAsJs
26588 * Shorthand method. `$sce.trustAsJs(value)` →
26589 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
26591 * @param {*} value The value to trustAs.
26592 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
26593 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
26594 * only accept expressions that are either literal constants or are the
26595 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
26600 * @name $sce#getTrusted
26603 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
26604 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
26605 * originally supplied value if the queried context type is a supertype of the created type.
26606 * If this condition isn't satisfied, throws an exception.
26608 * @param {string} type The kind of context in which this value is to be used.
26609 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
26611 * @returns {*} The value the was originally provided to
26612 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
26613 * Otherwise, throws an exception.
26618 * @name $sce#getTrustedHtml
26621 * Shorthand method. `$sce.getTrustedHtml(value)` →
26622 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
26624 * @param {*} value The value to pass to `$sce.getTrusted`.
26625 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
26630 * @name $sce#getTrustedCss
26633 * Shorthand method. `$sce.getTrustedCss(value)` →
26634 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
26636 * @param {*} value The value to pass to `$sce.getTrusted`.
26637 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
26642 * @name $sce#getTrustedUrl
26645 * Shorthand method. `$sce.getTrustedUrl(value)` →
26646 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
26648 * @param {*} value The value to pass to `$sce.getTrusted`.
26649 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
26654 * @name $sce#getTrustedResourceUrl
26657 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
26658 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
26660 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
26661 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
26666 * @name $sce#getTrustedJs
26669 * Shorthand method. `$sce.getTrustedJs(value)` →
26670 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
26672 * @param {*} value The value to pass to `$sce.getTrusted`.
26673 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
26678 * @name $sce#parseAsHtml
26681 * Shorthand method. `$sce.parseAsHtml(expression string)` →
26682 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
26684 * @param {string} expression String expression to compile.
26685 * @returns {function(context, locals)} a function which represents the compiled expression:
26687 * * `context` – `{object}` – an object against which any expressions embedded in the strings
26688 * are evaluated against (typically a scope object).
26689 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
26695 * @name $sce#parseAsCss
26698 * Shorthand method. `$sce.parseAsCss(value)` →
26699 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
26701 * @param {string} expression String expression to compile.
26702 * @returns {function(context, locals)} a function which represents the compiled expression:
26704 * * `context` – `{object}` – an object against which any expressions embedded in the strings
26705 * are evaluated against (typically a scope object).
26706 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
26712 * @name $sce#parseAsUrl
26715 * Shorthand method. `$sce.parseAsUrl(value)` →
26716 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
26718 * @param {string} expression String expression to compile.
26719 * @returns {function(context, locals)} a function which represents the compiled expression:
26721 * * `context` – `{object}` – an object against which any expressions embedded in the strings
26722 * are evaluated against (typically a scope object).
26723 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
26729 * @name $sce#parseAsResourceUrl
26732 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
26733 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
26735 * @param {string} expression String expression to compile.
26736 * @returns {function(context, locals)} a function which represents the compiled expression:
26738 * * `context` – `{object}` – an object against which any expressions embedded in the strings
26739 * are evaluated against (typically a scope object).
26740 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
26746 * @name $sce#parseAsJs
26749 * Shorthand method. `$sce.parseAsJs(value)` →
26750 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
26752 * @param {string} expression String expression to compile.
26753 * @returns {function(context, locals)} a function which represents the compiled expression:
26755 * * `context` – `{object}` – an object against which any expressions embedded in the strings
26756 * are evaluated against (typically a scope object).
26757 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
26761 // Shorthand delegations.
26762 var parse = sce.parseAs,
26763 getTrusted = sce.getTrusted,
26764 trustAs = sce.trustAs;
26766 forEach(SCE_CONTEXTS, function(enumValue, name) {
26767 var lName = lowercase(name);
26768 sce[camelCase("parse_as_" + lName)] = function(expr) {
26769 return parse(enumValue, expr);
26771 sce[camelCase("get_trusted_" + lName)] = function(value) {
26772 return getTrusted(enumValue, value);
26774 sce[camelCase("trust_as_" + lName)] = function(value) {
26775 return trustAs(enumValue, value);
26784 * !!! This is an undocumented "private" service !!!
26787 * @requires $window
26788 * @requires $document
26790 * @property {boolean} history Does the browser support html5 history api ?
26791 * @property {boolean} transitions Does the browser support CSS transition events ?
26792 * @property {boolean} animations Does the browser support CSS animation events ?
26795 * This is very simple implementation of testing browser's features.
26797 function $SnifferProvider() {
26798 this.$get = ['$window', '$document', function($window, $document) {
26799 var eventSupport = {},
26801 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
26802 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
26803 document = $document[0] || {},
26805 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
26806 bodyStyle = document.body && document.body.style,
26807 transitions = false,
26808 animations = false,
26812 for (var prop in bodyStyle) {
26813 if (match = vendorRegex.exec(prop)) {
26814 vendorPrefix = match[0];
26815 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
26820 if (!vendorPrefix) {
26821 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
26824 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
26825 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
26827 if (android && (!transitions || !animations)) {
26828 transitions = isString(bodyStyle.webkitTransition);
26829 animations = isString(bodyStyle.webkitAnimation);
26835 // Android has history.pushState, but it does not update location correctly
26836 // so let's not use the history API at all.
26837 // http://code.google.com/p/android/issues/detail?id=17471
26838 // https://github.com/angular/angular.js/issues/904
26840 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
26841 // so let's not use the history API also
26842 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
26844 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
26846 hasEvent: function(event) {
26847 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
26848 // it. In particular the event is not fired when backspace or delete key are pressed or
26849 // when cut operation is performed.
26850 // IE10+ implements 'input' event but it erroneously fires under various situations,
26851 // e.g. when placeholder changes, or a form is focused.
26852 if (event === 'input' && msie <= 11) return false;
26854 if (isUndefined(eventSupport[event])) {
26855 var divElm = document.createElement('div');
26856 eventSupport[event] = 'on' + event in divElm;
26859 return eventSupport[event];
26862 vendorPrefix: vendorPrefix,
26863 transitions: transitions,
26864 animations: animations,
26870 var $compileMinErr = minErr('$compile');
26874 * @name $templateRequest
26877 * The `$templateRequest` service runs security checks then downloads the provided template using
26878 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
26879 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
26880 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
26881 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
26882 * when `tpl` is of type string and `$templateCache` has the matching entry.
26884 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
26885 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
26887 * @return {Promise} a promise for the HTTP response data of the given URL.
26889 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
26891 function $TemplateRequestProvider() {
26892 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
26893 function handleRequestFn(tpl, ignoreRequestError) {
26894 handleRequestFn.totalPendingRequests++;
26896 // We consider the template cache holds only trusted templates, so
26897 // there's no need to go through whitelisting again for keys that already
26898 // are included in there. This also makes Angular accept any script
26899 // directive, no matter its name. However, we still need to unwrap trusted
26901 if (!isString(tpl) || !$templateCache.get(tpl)) {
26902 tpl = $sce.getTrustedResourceUrl(tpl);
26905 var transformResponse = $http.defaults && $http.defaults.transformResponse;
26907 if (isArray(transformResponse)) {
26908 transformResponse = transformResponse.filter(function(transformer) {
26909 return transformer !== defaultHttpResponseTransform;
26911 } else if (transformResponse === defaultHttpResponseTransform) {
26912 transformResponse = null;
26915 var httpOptions = {
26916 cache: $templateCache,
26917 transformResponse: transformResponse
26920 return $http.get(tpl, httpOptions)
26921 ['finally'](function() {
26922 handleRequestFn.totalPendingRequests--;
26924 .then(function(response) {
26925 $templateCache.put(tpl, response.data);
26926 return response.data;
26929 function handleError(resp) {
26930 if (!ignoreRequestError) {
26931 throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
26932 tpl, resp.status, resp.statusText);
26934 return $q.reject(resp);
26938 handleRequestFn.totalPendingRequests = 0;
26940 return handleRequestFn;
26944 function $$TestabilityProvider() {
26945 this.$get = ['$rootScope', '$browser', '$location',
26946 function($rootScope, $browser, $location) {
26949 * @name $testability
26952 * The private $$testability service provides a collection of methods for use when debugging
26953 * or by automated test and debugging tools.
26955 var testability = {};
26958 * @name $$testability#findBindings
26961 * Returns an array of elements that are bound (via ng-bind or {{}})
26962 * to expressions matching the input.
26964 * @param {Element} element The element root to search from.
26965 * @param {string} expression The binding expression to match.
26966 * @param {boolean} opt_exactMatch If true, only returns exact matches
26967 * for the expression. Filters and whitespace are ignored.
26969 testability.findBindings = function(element, expression, opt_exactMatch) {
26970 var bindings = element.getElementsByClassName('ng-binding');
26972 forEach(bindings, function(binding) {
26973 var dataBinding = angular.element(binding).data('$binding');
26975 forEach(dataBinding, function(bindingName) {
26976 if (opt_exactMatch) {
26977 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
26978 if (matcher.test(bindingName)) {
26979 matches.push(binding);
26982 if (bindingName.indexOf(expression) != -1) {
26983 matches.push(binding);
26993 * @name $$testability#findModels
26996 * Returns an array of elements that are two-way found via ng-model to
26997 * expressions matching the input.
26999 * @param {Element} element The element root to search from.
27000 * @param {string} expression The model expression to match.
27001 * @param {boolean} opt_exactMatch If true, only returns exact matches
27002 * for the expression.
27004 testability.findModels = function(element, expression, opt_exactMatch) {
27005 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
27006 for (var p = 0; p < prefixes.length; ++p) {
27007 var attributeEquals = opt_exactMatch ? '=' : '*=';
27008 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
27009 var elements = element.querySelectorAll(selector);
27010 if (elements.length) {
27017 * @name $$testability#getLocation
27020 * Shortcut for getting the location in a browser agnostic way. Returns
27021 * the path, search, and hash. (e.g. /path?a=b#hash)
27023 testability.getLocation = function() {
27024 return $location.url();
27028 * @name $$testability#setLocation
27031 * Shortcut for navigating to a location without doing a full page reload.
27033 * @param {string} url The location url (path, search and hash,
27034 * e.g. /path?a=b#hash) to go to.
27036 testability.setLocation = function(url) {
27037 if (url !== $location.url()) {
27038 $location.url(url);
27039 $rootScope.$digest();
27044 * @name $$testability#whenStable
27047 * Calls the callback when $timeout and $http requests are completed.
27049 * @param {function} callback
27051 testability.whenStable = function(callback) {
27052 $browser.notifyWhenNoOutstandingRequests(callback);
27055 return testability;
27059 function $TimeoutProvider() {
27060 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
27061 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
27063 var deferreds = {};
27071 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
27072 * block and delegates any exceptions to
27073 * {@link ng.$exceptionHandler $exceptionHandler} service.
27075 * The return value of calling `$timeout` is a promise, which will be resolved when
27076 * the delay has passed and the timeout function, if provided, is executed.
27078 * To cancel a timeout request, call `$timeout.cancel(promise)`.
27080 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
27081 * synchronously flush the queue of deferred functions.
27083 * If you only want a promise that will be resolved after some specified delay
27084 * then you can call `$timeout` without the `fn` function.
27086 * @param {function()=} fn A function, whose execution should be delayed.
27087 * @param {number=} [delay=0] Delay in milliseconds.
27088 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
27089 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
27090 * @param {...*=} Pass additional parameters to the executed function.
27091 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
27092 * promise will be resolved with is the return value of the `fn` function.
27095 function timeout(fn, delay, invokeApply) {
27096 if (!isFunction(fn)) {
27097 invokeApply = delay;
27102 var args = sliceArgs(arguments, 3),
27103 skipApply = (isDefined(invokeApply) && !invokeApply),
27104 deferred = (skipApply ? $$q : $q).defer(),
27105 promise = deferred.promise,
27108 timeoutId = $browser.defer(function() {
27110 deferred.resolve(fn.apply(null, args));
27112 deferred.reject(e);
27113 $exceptionHandler(e);
27116 delete deferreds[promise.$$timeoutId];
27119 if (!skipApply) $rootScope.$apply();
27122 promise.$$timeoutId = timeoutId;
27123 deferreds[timeoutId] = deferred;
27131 * @name $timeout#cancel
27134 * Cancels a task associated with the `promise`. As a result of this, the promise will be
27135 * resolved with a rejection.
27137 * @param {Promise=} promise Promise returned by the `$timeout` function.
27138 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
27141 timeout.cancel = function(promise) {
27142 if (promise && promise.$$timeoutId in deferreds) {
27143 deferreds[promise.$$timeoutId].reject('canceled');
27144 delete deferreds[promise.$$timeoutId];
27145 return $browser.defer.cancel(promise.$$timeoutId);
27154 // NOTE: The usage of window and document instead of $window and $document here is
27155 // deliberate. This service depends on the specific behavior of anchor nodes created by the
27156 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
27157 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
27158 // doesn't know about mocked locations and resolves URLs to the real document - which is
27159 // exactly the behavior needed here. There is little value is mocking these out for this
27161 var urlParsingNode = document.createElement("a");
27162 var originUrl = urlResolve(window.location.href);
27167 * Implementation Notes for non-IE browsers
27168 * ----------------------------------------
27169 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
27170 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
27171 * URL will be resolved into an absolute URL in the context of the application document.
27172 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
27173 * properties are all populated to reflect the normalized URL. This approach has wide
27174 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
27175 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
27177 * Implementation Notes for IE
27178 * ---------------------------
27179 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
27180 * browsers. However, the parsed components will not be set if the URL assigned did not specify
27181 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
27182 * work around that by performing the parsing in a 2nd step by taking a previously normalized
27183 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
27184 * properties such as protocol, hostname, port, etc.
27187 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
27188 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
27189 * http://url.spec.whatwg.org/#urlutils
27190 * https://github.com/angular/angular.js/pull/2902
27191 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
27194 * @param {string} url The URL to be parsed.
27195 * @description Normalizes and parses a URL.
27196 * @returns {object} Returns the normalized URL as a dictionary.
27198 * | member name | Description |
27199 * |---------------|----------------|
27200 * | href | A normalized version of the provided URL if it was not an absolute URL |
27201 * | protocol | The protocol including the trailing colon |
27202 * | host | The host and port (if the port is non-default) of the normalizedUrl |
27203 * | search | The search params, minus the question mark |
27204 * | hash | The hash string, minus the hash symbol
27205 * | hostname | The hostname
27206 * | port | The port, without ":"
27207 * | pathname | The pathname, beginning with "/"
27210 function urlResolve(url) {
27214 // Normalize before parse. Refer Implementation Notes on why this is
27215 // done in two steps on IE.
27216 urlParsingNode.setAttribute("href", href);
27217 href = urlParsingNode.href;
27220 urlParsingNode.setAttribute('href', href);
27222 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
27224 href: urlParsingNode.href,
27225 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
27226 host: urlParsingNode.host,
27227 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
27228 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
27229 hostname: urlParsingNode.hostname,
27230 port: urlParsingNode.port,
27231 pathname: (urlParsingNode.pathname.charAt(0) === '/')
27232 ? urlParsingNode.pathname
27233 : '/' + urlParsingNode.pathname
27238 * Parse a request URL and determine whether this is a same-origin request as the application document.
27240 * @param {string|object} requestUrl The url of the request as a string that will be resolved
27241 * or a parsed URL object.
27242 * @returns {boolean} Whether the request is for the same origin as the application document.
27244 function urlIsSameOrigin(requestUrl) {
27245 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
27246 return (parsed.protocol === originUrl.protocol &&
27247 parsed.host === originUrl.host);
27255 * A reference to the browser's `window` object. While `window`
27256 * is globally available in JavaScript, it causes testability problems, because
27257 * it is a global variable. In angular we always refer to it through the
27258 * `$window` service, so it may be overridden, removed or mocked for testing.
27260 * Expressions, like the one defined for the `ngClick` directive in the example
27261 * below, are evaluated with respect to the current scope. Therefore, there is
27262 * no risk of inadvertently coding in a dependency on a global value in such an
27266 <example module="windowExample">
27267 <file name="index.html">
27269 angular.module('windowExample', [])
27270 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
27271 $scope.greeting = 'Hello, World!';
27272 $scope.doGreeting = function(greeting) {
27273 $window.alert(greeting);
27277 <div ng-controller="ExampleController">
27278 <input type="text" ng-model="greeting" aria-label="greeting" />
27279 <button ng-click="doGreeting(greeting)">ALERT</button>
27282 <file name="protractor.js" type="protractor">
27283 it('should display the greeting in the input box', function() {
27284 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
27285 // If we click the button it will block the test runner
27286 // element(':button').click();
27291 function $WindowProvider() {
27292 this.$get = valueFn(window);
27296 * @name $$cookieReader
27297 * @requires $document
27300 * This is a private service for reading cookies used by $http and ngCookies
27302 * @return {Object} a key/value map of the current cookies
27304 function $$CookieReader($document) {
27305 var rawDocument = $document[0] || {};
27306 var lastCookies = {};
27307 var lastCookieString = '';
27309 function safeDecodeURIComponent(str) {
27311 return decodeURIComponent(str);
27317 return function() {
27318 var cookieArray, cookie, i, index, name;
27319 var currentCookieString = rawDocument.cookie || '';
27321 if (currentCookieString !== lastCookieString) {
27322 lastCookieString = currentCookieString;
27323 cookieArray = lastCookieString.split('; ');
27326 for (i = 0; i < cookieArray.length; i++) {
27327 cookie = cookieArray[i];
27328 index = cookie.indexOf('=');
27329 if (index > 0) { //ignore nameless cookies
27330 name = safeDecodeURIComponent(cookie.substring(0, index));
27331 // the first value that is seen for a cookie is the most
27332 // specific one. values for the same cookie name that
27333 // follow are for less specific paths.
27334 if (isUndefined(lastCookies[name])) {
27335 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
27340 return lastCookies;
27344 $$CookieReader.$inject = ['$document'];
27346 function $$CookieReaderProvider() {
27347 this.$get = $$CookieReader;
27350 /* global currencyFilter: true,
27352 filterFilter: true,
27354 limitToFilter: true,
27355 lowercaseFilter: true,
27356 numberFilter: true,
27357 orderByFilter: true,
27358 uppercaseFilter: true,
27363 * @name $filterProvider
27366 * Filters are just functions which transform input to an output. However filters need to be
27367 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
27368 * annotated with dependencies and is responsible for creating a filter function.
27370 * <div class="alert alert-warning">
27371 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
27372 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
27373 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
27374 * (`myapp_subsection_filterx`).
27378 * // Filter registration
27379 * function MyModule($provide, $filterProvider) {
27380 * // create a service to demonstrate injection (not always needed)
27381 * $provide.value('greet', function(name){
27382 * return 'Hello ' + name + '!';
27385 * // register a filter factory which uses the
27386 * // greet service to demonstrate DI.
27387 * $filterProvider.register('greet', function(greet){
27388 * // return the filter function which uses the greet service
27389 * // to generate salutation
27390 * return function(text) {
27391 * // filters need to be forgiving so check input validity
27392 * return text && greet(text) || text;
27398 * The filter function is registered with the `$injector` under the filter name suffix with
27402 * it('should be the same instance', inject(
27403 * function($filterProvider) {
27404 * $filterProvider.register('reverse', function(){
27408 * function($filter, reverseFilter) {
27409 * expect($filter('reverse')).toBe(reverseFilter);
27414 * For more information about how angular filters work, and how to create your own filters, see
27415 * {@link guide/filter Filters} in the Angular Developer Guide.
27423 * Filters are used for formatting data displayed to the user.
27425 * The general syntax in templates is as follows:
27427 * {{ expression [| filter_name[:parameter_value] ... ] }}
27429 * @param {String} name Name of the filter function to retrieve
27430 * @return {Function} the filter function
27432 <example name="$filter" module="filterExample">
27433 <file name="index.html">
27434 <div ng-controller="MainCtrl">
27435 <h3>{{ originalText }}</h3>
27436 <h3>{{ filteredText }}</h3>
27440 <file name="script.js">
27441 angular.module('filterExample', [])
27442 .controller('MainCtrl', function($scope, $filter) {
27443 $scope.originalText = 'hello';
27444 $scope.filteredText = $filter('uppercase')($scope.originalText);
27449 $FilterProvider.$inject = ['$provide'];
27450 function $FilterProvider($provide) {
27451 var suffix = 'Filter';
27455 * @name $filterProvider#register
27456 * @param {string|Object} name Name of the filter function, or an object map of filters where
27457 * the keys are the filter names and the values are the filter factories.
27459 * <div class="alert alert-warning">
27460 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
27461 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
27462 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
27463 * (`myapp_subsection_filterx`).
27465 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
27466 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
27467 * of the registered filter instances.
27469 function register(name, factory) {
27470 if (isObject(name)) {
27472 forEach(name, function(filter, key) {
27473 filters[key] = register(key, filter);
27477 return $provide.factory(name + suffix, factory);
27480 this.register = register;
27482 this.$get = ['$injector', function($injector) {
27483 return function(name) {
27484 return $injector.get(name + suffix);
27488 ////////////////////////////////////////
27491 currencyFilter: false,
27493 filterFilter: false,
27495 limitToFilter: false,
27496 lowercaseFilter: false,
27497 numberFilter: false,
27498 orderByFilter: false,
27499 uppercaseFilter: false,
27502 register('currency', currencyFilter);
27503 register('date', dateFilter);
27504 register('filter', filterFilter);
27505 register('json', jsonFilter);
27506 register('limitTo', limitToFilter);
27507 register('lowercase', lowercaseFilter);
27508 register('number', numberFilter);
27509 register('orderBy', orderByFilter);
27510 register('uppercase', uppercaseFilter);
27519 * Selects a subset of items from `array` and returns it as a new array.
27521 * @param {Array} array The source array.
27522 * @param {string|Object|function()} expression The predicate to be used for selecting items from
27527 * - `string`: The string is used for matching against the contents of the `array`. All strings or
27528 * objects with string properties in `array` that match this string will be returned. This also
27529 * applies to nested object properties.
27530 * The predicate can be negated by prefixing the string with `!`.
27532 * - `Object`: A pattern object can be used to filter specific properties on objects contained
27533 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
27534 * which have property `name` containing "M" and property `phone` containing "1". A special
27535 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
27536 * property of the object or its nested object properties. That's equivalent to the simple
27537 * substring match with a `string` as described above. The predicate can be negated by prefixing
27538 * the string with `!`.
27539 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
27540 * not containing "M".
27542 * Note that a named property will match properties on the same level only, while the special
27543 * `$` property will match properties on the same level or deeper. E.g. an array item like
27544 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
27545 * **will** be matched by `{$: 'John'}`.
27547 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
27548 * The function is called for each element of the array, with the element, its index, and
27549 * the entire array itself as arguments.
27551 * The final result is an array of those elements that the predicate returned true for.
27553 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
27554 * determining if the expected value (from the filter expression) and actual value (from
27555 * the object in the array) should be considered a match.
27559 * - `function(actual, expected)`:
27560 * The function will be given the object value and the predicate value to compare and
27561 * should return true if both values should be considered equal.
27563 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
27564 * This is essentially strict comparison of expected and actual.
27566 * - `false|undefined`: A short hand for a function which will look for a substring match in case
27569 * Primitive values are converted to strings. Objects are not compared against primitives,
27570 * unless they have a custom `toString` method (e.g. `Date` objects).
27574 <file name="index.html">
27575 <div ng-init="friends = [{name:'John', phone:'555-1276'},
27576 {name:'Mary', phone:'800-BIG-MARY'},
27577 {name:'Mike', phone:'555-4321'},
27578 {name:'Adam', phone:'555-5678'},
27579 {name:'Julie', phone:'555-8765'},
27580 {name:'Juliette', phone:'555-5678'}]"></div>
27582 <label>Search: <input ng-model="searchText"></label>
27583 <table id="searchTextResults">
27584 <tr><th>Name</th><th>Phone</th></tr>
27585 <tr ng-repeat="friend in friends | filter:searchText">
27586 <td>{{friend.name}}</td>
27587 <td>{{friend.phone}}</td>
27591 <label>Any: <input ng-model="search.$"></label> <br>
27592 <label>Name only <input ng-model="search.name"></label><br>
27593 <label>Phone only <input ng-model="search.phone"></label><br>
27594 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
27595 <table id="searchObjResults">
27596 <tr><th>Name</th><th>Phone</th></tr>
27597 <tr ng-repeat="friendObj in friends | filter:search:strict">
27598 <td>{{friendObj.name}}</td>
27599 <td>{{friendObj.phone}}</td>
27603 <file name="protractor.js" type="protractor">
27604 var expectFriendNames = function(expectedNames, key) {
27605 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
27606 arr.forEach(function(wd, i) {
27607 expect(wd.getText()).toMatch(expectedNames[i]);
27612 it('should search across all fields when filtering with a string', function() {
27613 var searchText = element(by.model('searchText'));
27614 searchText.clear();
27615 searchText.sendKeys('m');
27616 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
27618 searchText.clear();
27619 searchText.sendKeys('76');
27620 expectFriendNames(['John', 'Julie'], 'friend');
27623 it('should search in specific fields when filtering with a predicate object', function() {
27624 var searchAny = element(by.model('search.$'));
27626 searchAny.sendKeys('i');
27627 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
27629 it('should use a equal comparison when comparator is true', function() {
27630 var searchName = element(by.model('search.name'));
27631 var strict = element(by.model('strict'));
27632 searchName.clear();
27633 searchName.sendKeys('Julie');
27635 expectFriendNames(['Julie'], 'friendObj');
27640 function filterFilter() {
27641 return function(array, expression, comparator) {
27642 if (!isArrayLike(array)) {
27643 if (array == null) {
27646 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
27650 var expressionType = getTypeForFilter(expression);
27652 var matchAgainstAnyProp;
27654 switch (expressionType) {
27656 predicateFn = expression;
27662 matchAgainstAnyProp = true;
27666 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
27672 return Array.prototype.filter.call(array, predicateFn);
27676 // Helper functions for `filterFilter`
27677 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
27678 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
27681 if (comparator === true) {
27682 comparator = equals;
27683 } else if (!isFunction(comparator)) {
27684 comparator = function(actual, expected) {
27685 if (isUndefined(actual)) {
27686 // No substring matching against `undefined`
27689 if ((actual === null) || (expected === null)) {
27690 // No substring matching against `null`; only match against `null`
27691 return actual === expected;
27693 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
27694 // Should not compare primitives against objects, unless they have custom `toString` method
27698 actual = lowercase('' + actual);
27699 expected = lowercase('' + expected);
27700 return actual.indexOf(expected) !== -1;
27704 predicateFn = function(item) {
27705 if (shouldMatchPrimitives && !isObject(item)) {
27706 return deepCompare(item, expression.$, comparator, false);
27708 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
27711 return predicateFn;
27714 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
27715 var actualType = getTypeForFilter(actual);
27716 var expectedType = getTypeForFilter(expected);
27718 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
27719 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
27720 } else if (isArray(actual)) {
27721 // In case `actual` is an array, consider it a match
27722 // if ANY of it's items matches `expected`
27723 return actual.some(function(item) {
27724 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
27728 switch (actualType) {
27731 if (matchAgainstAnyProp) {
27732 for (key in actual) {
27733 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
27737 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
27738 } else if (expectedType === 'object') {
27739 for (key in expected) {
27740 var expectedVal = expected[key];
27741 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
27745 var matchAnyProperty = key === '$';
27746 var actualVal = matchAnyProperty ? actual : actual[key];
27747 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
27753 return comparator(actual, expected);
27759 return comparator(actual, expected);
27763 // Used for easily differentiating between `null` and actual `object`
27764 function getTypeForFilter(val) {
27765 return (val === null) ? 'null' : typeof val;
27774 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
27775 * symbol for current locale is used.
27777 * @param {number} amount Input to filter.
27778 * @param {string=} symbol Currency symbol or identifier to be displayed.
27779 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
27780 * @returns {string} Formatted number.
27784 <example module="currencyExample">
27785 <file name="index.html">
27787 angular.module('currencyExample', [])
27788 .controller('ExampleController', ['$scope', function($scope) {
27789 $scope.amount = 1234.56;
27792 <div ng-controller="ExampleController">
27793 <input type="number" ng-model="amount" aria-label="amount"> <br>
27794 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
27795 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
27796 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
27799 <file name="protractor.js" type="protractor">
27800 it('should init with 1234.56', function() {
27801 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
27802 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
27803 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
27805 it('should update', function() {
27806 if (browser.params.browser == 'safari') {
27807 // Safari does not understand the minus key. See
27808 // https://github.com/angular/protractor/issues/481
27811 element(by.model('amount')).clear();
27812 element(by.model('amount')).sendKeys('-1234');
27813 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
27814 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
27815 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
27820 currencyFilter.$inject = ['$locale'];
27821 function currencyFilter($locale) {
27822 var formats = $locale.NUMBER_FORMATS;
27823 return function(amount, currencySymbol, fractionSize) {
27824 if (isUndefined(currencySymbol)) {
27825 currencySymbol = formats.CURRENCY_SYM;
27828 if (isUndefined(fractionSize)) {
27829 fractionSize = formats.PATTERNS[1].maxFrac;
27832 // if null or undefined pass it through
27833 return (amount == null)
27835 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
27836 replace(/\u00A4/g, currencySymbol);
27846 * Formats a number as text.
27848 * If the input is null or undefined, it will just be returned.
27849 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
27850 * If the input is not a number an empty string is returned.
27853 * @param {number|string} number Number to format.
27854 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
27855 * If this is not provided then the fraction size is computed from the current locale's number
27856 * formatting pattern. In the case of the default locale, it will be 3.
27857 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
27860 <example module="numberFilterExample">
27861 <file name="index.html">
27863 angular.module('numberFilterExample', [])
27864 .controller('ExampleController', ['$scope', function($scope) {
27865 $scope.val = 1234.56789;
27868 <div ng-controller="ExampleController">
27869 <label>Enter number: <input ng-model='val'></label><br>
27870 Default formatting: <span id='number-default'>{{val | number}}</span><br>
27871 No fractions: <span>{{val | number:0}}</span><br>
27872 Negative number: <span>{{-val | number:4}}</span>
27875 <file name="protractor.js" type="protractor">
27876 it('should format numbers', function() {
27877 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
27878 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
27879 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
27882 it('should update', function() {
27883 element(by.model('val')).clear();
27884 element(by.model('val')).sendKeys('3374.333');
27885 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
27886 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
27887 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
27894 numberFilter.$inject = ['$locale'];
27895 function numberFilter($locale) {
27896 var formats = $locale.NUMBER_FORMATS;
27897 return function(number, fractionSize) {
27899 // if null or undefined pass it through
27900 return (number == null)
27902 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
27907 var DECIMAL_SEP = '.';
27908 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
27909 if (isObject(number)) return '';
27911 var isNegative = number < 0;
27912 number = Math.abs(number);
27914 var isInfinity = number === Infinity;
27915 if (!isInfinity && !isFinite(number)) return '';
27917 var numStr = number + '',
27919 hasExponent = false,
27922 if (isInfinity) formatedText = '\u221e';
27924 if (!isInfinity && numStr.indexOf('e') !== -1) {
27925 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
27926 if (match && match[2] == '-' && match[3] > fractionSize + 1) {
27929 formatedText = numStr;
27930 hasExponent = true;
27934 if (!isInfinity && !hasExponent) {
27935 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
27937 // determine fractionSize if it is not specified
27938 if (isUndefined(fractionSize)) {
27939 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
27942 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
27944 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
27945 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
27947 var fraction = ('' + number).split(DECIMAL_SEP);
27948 var whole = fraction[0];
27949 fraction = fraction[1] || '';
27952 lgroup = pattern.lgSize,
27953 group = pattern.gSize;
27955 if (whole.length >= (lgroup + group)) {
27956 pos = whole.length - lgroup;
27957 for (i = 0; i < pos; i++) {
27958 if ((pos - i) % group === 0 && i !== 0) {
27959 formatedText += groupSep;
27961 formatedText += whole.charAt(i);
27965 for (i = pos; i < whole.length; i++) {
27966 if ((whole.length - i) % lgroup === 0 && i !== 0) {
27967 formatedText += groupSep;
27969 formatedText += whole.charAt(i);
27972 // format fraction part.
27973 while (fraction.length < fractionSize) {
27977 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
27979 if (fractionSize > 0 && number < 1) {
27980 formatedText = number.toFixed(fractionSize);
27981 number = parseFloat(formatedText);
27982 formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
27986 if (number === 0) {
27987 isNegative = false;
27990 parts.push(isNegative ? pattern.negPre : pattern.posPre,
27992 isNegative ? pattern.negSuf : pattern.posSuf);
27993 return parts.join('');
27996 function padNumber(num, digits, trim) {
28003 while (num.length < digits) num = '0' + num;
28005 num = num.substr(num.length - digits);
28011 function dateGetter(name, size, offset, trim) {
28012 offset = offset || 0;
28013 return function(date) {
28014 var value = date['get' + name]();
28015 if (offset > 0 || value > -offset) {
28018 if (value === 0 && offset == -12) value = 12;
28019 return padNumber(value, size, trim);
28023 function dateStrGetter(name, shortForm) {
28024 return function(date, formats) {
28025 var value = date['get' + name]();
28026 var get = uppercase(shortForm ? ('SHORT' + name) : name);
28028 return formats[get][value];
28032 function timeZoneGetter(date, formats, offset) {
28033 var zone = -1 * offset;
28034 var paddedZone = (zone >= 0) ? "+" : "";
28036 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
28037 padNumber(Math.abs(zone % 60), 2);
28042 function getFirstThursdayOfYear(year) {
28043 // 0 = index of January
28044 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
28045 // 4 = index of Thursday (+1 to account for 1st = 5)
28046 // 11 = index of *next* Thursday (+1 account for 1st = 12)
28047 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
28050 function getThursdayThisWeek(datetime) {
28051 return new Date(datetime.getFullYear(), datetime.getMonth(),
28052 // 4 = index of Thursday
28053 datetime.getDate() + (4 - datetime.getDay()));
28056 function weekGetter(size) {
28057 return function(date) {
28058 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
28059 thisThurs = getThursdayThisWeek(date);
28061 var diff = +thisThurs - +firstThurs,
28062 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
28064 return padNumber(result, size);
28068 function ampmGetter(date, formats) {
28069 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
28072 function eraGetter(date, formats) {
28073 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
28076 function longEraGetter(date, formats) {
28077 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
28080 var DATE_FORMATS = {
28081 yyyy: dateGetter('FullYear', 4),
28082 yy: dateGetter('FullYear', 2, 0, true),
28083 y: dateGetter('FullYear', 1),
28084 MMMM: dateStrGetter('Month'),
28085 MMM: dateStrGetter('Month', true),
28086 MM: dateGetter('Month', 2, 1),
28087 M: dateGetter('Month', 1, 1),
28088 dd: dateGetter('Date', 2),
28089 d: dateGetter('Date', 1),
28090 HH: dateGetter('Hours', 2),
28091 H: dateGetter('Hours', 1),
28092 hh: dateGetter('Hours', 2, -12),
28093 h: dateGetter('Hours', 1, -12),
28094 mm: dateGetter('Minutes', 2),
28095 m: dateGetter('Minutes', 1),
28096 ss: dateGetter('Seconds', 2),
28097 s: dateGetter('Seconds', 1),
28098 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
28099 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
28100 sss: dateGetter('Milliseconds', 3),
28101 EEEE: dateStrGetter('Day'),
28102 EEE: dateStrGetter('Day', true),
28110 GGGG: longEraGetter
28113 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
28114 NUMBER_STRING = /^\-?\d+$/;
28122 * Formats `date` to a string based on the requested `format`.
28124 * `format` string can be composed of the following elements:
28126 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
28127 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
28128 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
28129 * * `'MMMM'`: Month in year (January-December)
28130 * * `'MMM'`: Month in year (Jan-Dec)
28131 * * `'MM'`: Month in year, padded (01-12)
28132 * * `'M'`: Month in year (1-12)
28133 * * `'dd'`: Day in month, padded (01-31)
28134 * * `'d'`: Day in month (1-31)
28135 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
28136 * * `'EEE'`: Day in Week, (Sun-Sat)
28137 * * `'HH'`: Hour in day, padded (00-23)
28138 * * `'H'`: Hour in day (0-23)
28139 * * `'hh'`: Hour in AM/PM, padded (01-12)
28140 * * `'h'`: Hour in AM/PM, (1-12)
28141 * * `'mm'`: Minute in hour, padded (00-59)
28142 * * `'m'`: Minute in hour (0-59)
28143 * * `'ss'`: Second in minute, padded (00-59)
28144 * * `'s'`: Second in minute (0-59)
28145 * * `'sss'`: Millisecond in second, padded (000-999)
28146 * * `'a'`: AM/PM marker
28147 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
28148 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
28149 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
28150 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
28151 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
28153 * `format` string can also be one of the following predefined
28154 * {@link guide/i18n localizable formats}:
28156 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
28157 * (e.g. Sep 3, 2010 12:05:08 PM)
28158 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
28159 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
28160 * (e.g. Friday, September 3, 2010)
28161 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
28162 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
28163 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
28164 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
28165 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
28167 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
28168 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
28169 * (e.g. `"h 'o''clock'"`).
28171 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
28172 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
28173 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
28174 * specified in the string input, the time is considered to be in the local timezone.
28175 * @param {string=} format Formatting rules (see Description). If not specified,
28176 * `mediumDate` is used.
28177 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
28178 * continental US time zone abbreviations, but for general use, use a time zone offset, for
28179 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
28180 * If not specified, the timezone of the browser will be used.
28181 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
28185 <file name="index.html">
28186 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
28187 <span>{{1288323623006 | date:'medium'}}</span><br>
28188 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
28189 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
28190 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
28191 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
28192 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
28193 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
28195 <file name="protractor.js" type="protractor">
28196 it('should format date', function() {
28197 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
28198 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
28199 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
28200 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
28201 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
28202 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
28203 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
28204 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
28209 dateFilter.$inject = ['$locale'];
28210 function dateFilter($locale) {
28213 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
28214 // 1 2 3 4 5 6 7 8 9 10 11
28215 function jsonStringToDate(string) {
28217 if (match = string.match(R_ISO8601_STR)) {
28218 var date = new Date(0),
28221 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
28222 timeSetter = match[8] ? date.setUTCHours : date.setHours;
28225 tzHour = toInt(match[9] + match[10]);
28226 tzMin = toInt(match[9] + match[11]);
28228 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
28229 var h = toInt(match[4] || 0) - tzHour;
28230 var m = toInt(match[5] || 0) - tzMin;
28231 var s = toInt(match[6] || 0);
28232 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
28233 timeSetter.call(date, h, m, s, ms);
28240 return function(date, format, timezone) {
28245 format = format || 'mediumDate';
28246 format = $locale.DATETIME_FORMATS[format] || format;
28247 if (isString(date)) {
28248 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
28251 if (isNumber(date)) {
28252 date = new Date(date);
28255 if (!isDate(date) || !isFinite(date.getTime())) {
28260 match = DATE_FORMATS_SPLIT.exec(format);
28262 parts = concat(parts, match, 1);
28263 format = parts.pop();
28265 parts.push(format);
28270 var dateTimezoneOffset = date.getTimezoneOffset();
28272 dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
28273 date = convertTimezoneToLocal(date, timezone, true);
28275 forEach(parts, function(value) {
28276 fn = DATE_FORMATS[value];
28277 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
28278 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
28292 * Allows you to convert a JavaScript object into JSON string.
28294 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
28295 * the binding is automatically converted to JSON.
28297 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
28298 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
28299 * @returns {string} JSON string.
28304 <file name="index.html">
28305 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
28306 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
28308 <file name="protractor.js" type="protractor">
28309 it('should jsonify filtered objects', function() {
28310 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
28311 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
28317 function jsonFilter() {
28318 return function(object, spacing) {
28319 if (isUndefined(spacing)) {
28322 return toJson(object, spacing);
28332 * Converts string to lowercase.
28333 * @see angular.lowercase
28335 var lowercaseFilter = valueFn(lowercase);
28343 * Converts string to uppercase.
28344 * @see angular.uppercase
28346 var uppercaseFilter = valueFn(uppercase);
28354 * Creates a new array or string containing only a specified number of elements. The elements
28355 * are taken from either the beginning or the end of the source array, string or number, as specified by
28356 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
28357 * converted to a string.
28359 * @param {Array|string|number} input Source array, string or number to be limited.
28360 * @param {string|number} limit The length of the returned array or string. If the `limit` number
28361 * is positive, `limit` number of items from the beginning of the source array/string are copied.
28362 * If the number is negative, `limit` number of items from the end of the source array/string
28363 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
28364 * the input will be returned unchanged.
28365 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
28366 * indicates an offset from the end of `input`. Defaults to `0`.
28367 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
28368 * had less than `limit` elements.
28371 <example module="limitToExample">
28372 <file name="index.html">
28374 angular.module('limitToExample', [])
28375 .controller('ExampleController', ['$scope', function($scope) {
28376 $scope.numbers = [1,2,3,4,5,6,7,8,9];
28377 $scope.letters = "abcdefghi";
28378 $scope.longNumber = 2345432342;
28379 $scope.numLimit = 3;
28380 $scope.letterLimit = 3;
28381 $scope.longNumberLimit = 3;
28384 <div ng-controller="ExampleController">
28386 Limit {{numbers}} to:
28387 <input type="number" step="1" ng-model="numLimit">
28389 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
28391 Limit {{letters}} to:
28392 <input type="number" step="1" ng-model="letterLimit">
28394 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
28396 Limit {{longNumber}} to:
28397 <input type="number" step="1" ng-model="longNumberLimit">
28399 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
28402 <file name="protractor.js" type="protractor">
28403 var numLimitInput = element(by.model('numLimit'));
28404 var letterLimitInput = element(by.model('letterLimit'));
28405 var longNumberLimitInput = element(by.model('longNumberLimit'));
28406 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
28407 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
28408 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
28410 it('should limit the number array to first three items', function() {
28411 expect(numLimitInput.getAttribute('value')).toBe('3');
28412 expect(letterLimitInput.getAttribute('value')).toBe('3');
28413 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
28414 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
28415 expect(limitedLetters.getText()).toEqual('Output letters: abc');
28416 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
28419 // There is a bug in safari and protractor that doesn't like the minus key
28420 // it('should update the output when -3 is entered', function() {
28421 // numLimitInput.clear();
28422 // numLimitInput.sendKeys('-3');
28423 // letterLimitInput.clear();
28424 // letterLimitInput.sendKeys('-3');
28425 // longNumberLimitInput.clear();
28426 // longNumberLimitInput.sendKeys('-3');
28427 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
28428 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
28429 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
28432 it('should not exceed the maximum size of input array', function() {
28433 numLimitInput.clear();
28434 numLimitInput.sendKeys('100');
28435 letterLimitInput.clear();
28436 letterLimitInput.sendKeys('100');
28437 longNumberLimitInput.clear();
28438 longNumberLimitInput.sendKeys('100');
28439 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
28440 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
28441 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
28446 function limitToFilter() {
28447 return function(input, limit, begin) {
28448 if (Math.abs(Number(limit)) === Infinity) {
28449 limit = Number(limit);
28451 limit = toInt(limit);
28453 if (isNaN(limit)) return input;
28455 if (isNumber(input)) input = input.toString();
28456 if (!isArray(input) && !isString(input)) return input;
28458 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
28459 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
28462 return input.slice(begin, begin + limit);
28465 return input.slice(limit, input.length);
28467 return input.slice(Math.max(0, begin + limit), begin);
28479 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
28480 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
28481 * as expected, make sure they are actually being saved as numbers and not strings.
28483 * @param {Array} array The array to sort.
28484 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
28485 * used by the comparator to determine the order of elements.
28489 * - `function`: Getter function. The result of this function will be sorted using the
28490 * `<`, `===`, `>` operator.
28491 * - `string`: An Angular expression. The result of this expression is used to compare elements
28492 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
28493 * 3 first characters of a property called `name`). The result of a constant expression
28494 * is interpreted as a property name to be used in comparisons (for example `"special name"`
28495 * to sort object by the value of their `special name` property). An expression can be
28496 * optionally prefixed with `+` or `-` to control ascending or descending sort order
28497 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
28498 * element itself is used to compare where sorting.
28499 * - `Array`: An array of function or string predicates. The first predicate in the array
28500 * is used for sorting, but when two items are equivalent, the next predicate is used.
28502 * If the predicate is missing or empty then it defaults to `'+'`.
28504 * @param {boolean=} reverse Reverse the order of the array.
28505 * @returns {Array} Sorted copy of the source array.
28509 * The example below demonstrates a simple ngRepeat, where the data is sorted
28510 * by age in descending order (predicate is set to `'-age'`).
28511 * `reverse` is not set, which means it defaults to `false`.
28512 <example module="orderByExample">
28513 <file name="index.html">
28515 angular.module('orderByExample', [])
28516 .controller('ExampleController', ['$scope', function($scope) {
28518 [{name:'John', phone:'555-1212', age:10},
28519 {name:'Mary', phone:'555-9876', age:19},
28520 {name:'Mike', phone:'555-4321', age:21},
28521 {name:'Adam', phone:'555-5678', age:35},
28522 {name:'Julie', phone:'555-8765', age:29}];
28525 <div ng-controller="ExampleController">
28526 <table class="friend">
28529 <th>Phone Number</th>
28532 <tr ng-repeat="friend in friends | orderBy:'-age'">
28533 <td>{{friend.name}}</td>
28534 <td>{{friend.phone}}</td>
28535 <td>{{friend.age}}</td>
28542 * The predicate and reverse parameters can be controlled dynamically through scope properties,
28543 * as shown in the next example.
28545 <example module="orderByExample">
28546 <file name="index.html">
28548 angular.module('orderByExample', [])
28549 .controller('ExampleController', ['$scope', function($scope) {
28551 [{name:'John', phone:'555-1212', age:10},
28552 {name:'Mary', phone:'555-9876', age:19},
28553 {name:'Mike', phone:'555-4321', age:21},
28554 {name:'Adam', phone:'555-5678', age:35},
28555 {name:'Julie', phone:'555-8765', age:29}];
28556 $scope.predicate = 'age';
28557 $scope.reverse = true;
28558 $scope.order = function(predicate) {
28559 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
28560 $scope.predicate = predicate;
28564 <style type="text/css">
28568 .sortorder.reverse:after {
28572 <div ng-controller="ExampleController">
28573 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
28575 [ <a href="" ng-click="predicate=''">unsorted</a> ]
28576 <table class="friend">
28579 <a href="" ng-click="order('name')">Name</a>
28580 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
28583 <a href="" ng-click="order('phone')">Phone Number</a>
28584 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
28587 <a href="" ng-click="order('age')">Age</a>
28588 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
28591 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
28592 <td>{{friend.name}}</td>
28593 <td>{{friend.phone}}</td>
28594 <td>{{friend.age}}</td>
28601 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
28602 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
28603 * desired parameters.
28608 <example module="orderByExample">
28609 <file name="index.html">
28610 <div ng-controller="ExampleController">
28611 <table class="friend">
28613 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
28614 (<a href="" ng-click="order('-name',false)">^</a>)</th>
28615 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
28616 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
28618 <tr ng-repeat="friend in friends">
28619 <td>{{friend.name}}</td>
28620 <td>{{friend.phone}}</td>
28621 <td>{{friend.age}}</td>
28627 <file name="script.js">
28628 angular.module('orderByExample', [])
28629 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
28630 var orderBy = $filter('orderBy');
28632 { name: 'John', phone: '555-1212', age: 10 },
28633 { name: 'Mary', phone: '555-9876', age: 19 },
28634 { name: 'Mike', phone: '555-4321', age: 21 },
28635 { name: 'Adam', phone: '555-5678', age: 35 },
28636 { name: 'Julie', phone: '555-8765', age: 29 }
28638 $scope.order = function(predicate, reverse) {
28639 $scope.friends = orderBy($scope.friends, predicate, reverse);
28641 $scope.order('-age',false);
28646 orderByFilter.$inject = ['$parse'];
28647 function orderByFilter($parse) {
28648 return function(array, sortPredicate, reverseOrder) {
28650 if (!(isArrayLike(array))) return array;
28652 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
28653 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
28655 var predicates = processPredicates(sortPredicate, reverseOrder);
28656 // Add a predicate at the end that evaluates to the element index. This makes the
28657 // sort stable as it works as a tie-breaker when all the input predicates cannot
28658 // distinguish between two elements.
28659 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
28661 // The next three lines are a version of a Swartzian Transform idiom from Perl
28662 // (sometimes called the Decorate-Sort-Undecorate idiom)
28663 // See https://en.wikipedia.org/wiki/Schwartzian_transform
28664 var compareValues = Array.prototype.map.call(array, getComparisonObject);
28665 compareValues.sort(doComparison);
28666 array = compareValues.map(function(item) { return item.value; });
28670 function getComparisonObject(value, index) {
28673 predicateValues: predicates.map(function(predicate) {
28674 return getPredicateValue(predicate.get(value), index);
28679 function doComparison(v1, v2) {
28681 for (var index=0, length = predicates.length; index < length; ++index) {
28682 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
28689 function processPredicates(sortPredicate, reverseOrder) {
28690 reverseOrder = reverseOrder ? -1 : 1;
28691 return sortPredicate.map(function(predicate) {
28692 var descending = 1, get = identity;
28694 if (isFunction(predicate)) {
28696 } else if (isString(predicate)) {
28697 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
28698 descending = predicate.charAt(0) == '-' ? -1 : 1;
28699 predicate = predicate.substring(1);
28701 if (predicate !== '') {
28702 get = $parse(predicate);
28703 if (get.constant) {
28705 get = function(value) { return value[key]; };
28709 return { get: get, descending: descending * reverseOrder };
28713 function isPrimitive(value) {
28714 switch (typeof value) {
28715 case 'number': /* falls through */
28716 case 'boolean': /* falls through */
28724 function objectValue(value, index) {
28725 // If `valueOf` is a valid function use that
28726 if (typeof value.valueOf === 'function') {
28727 value = value.valueOf();
28728 if (isPrimitive(value)) return value;
28730 // If `toString` is a valid function and not the one from `Object.prototype` use that
28731 if (hasCustomToString(value)) {
28732 value = value.toString();
28733 if (isPrimitive(value)) return value;
28735 // We have a basic object so we use the position of the object in the collection
28739 function getPredicateValue(value, index) {
28740 var type = typeof value;
28741 if (value === null) {
28744 } else if (type === 'string') {
28745 value = value.toLowerCase();
28746 } else if (type === 'object') {
28747 value = objectValue(value, index);
28749 return { value: value, type: type };
28752 function compare(v1, v2) {
28754 if (v1.type === v2.type) {
28755 if (v1.value !== v2.value) {
28756 result = v1.value < v2.value ? -1 : 1;
28759 result = v1.type < v2.type ? -1 : 1;
28765 function ngDirective(directive) {
28766 if (isFunction(directive)) {
28771 directive.restrict = directive.restrict || 'AC';
28772 return valueFn(directive);
28781 * Modifies the default behavior of the html A tag so that the default action is prevented when
28782 * the href attribute is empty.
28784 * This change permits the easy creation of action links with the `ngClick` directive
28785 * without changing the location or causing page reloads, e.g.:
28786 * `<a href="" ng-click="list.addItem()">Add Item</a>`
28788 var htmlAnchorDirective = valueFn({
28790 compile: function(element, attr) {
28791 if (!attr.href && !attr.xlinkHref) {
28792 return function(scope, element) {
28793 // If the linked element is not an anchor tag anymore, do nothing
28794 if (element[0].nodeName.toLowerCase() !== 'a') return;
28796 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
28797 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
28798 'xlink:href' : 'href';
28799 element.on('click', function(event) {
28800 // if we have no href url, then don't navigate anywhere.
28801 if (!element.attr(href)) {
28802 event.preventDefault();
28817 * Using Angular markup like `{{hash}}` in an href attribute will
28818 * make the link go to the wrong URL if the user clicks it before
28819 * Angular has a chance to replace the `{{hash}}` markup with its
28820 * value. Until Angular replaces the markup the link will be broken
28821 * and will most likely return a 404 error. The `ngHref` directive
28822 * solves this problem.
28824 * The wrong way to write it:
28826 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
28829 * The correct way to write it:
28831 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
28835 * @param {template} ngHref any string which can contain `{{}}` markup.
28838 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
28839 * in links and their different behaviors:
28841 <file name="index.html">
28842 <input ng-model="value" /><br />
28843 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
28844 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
28845 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
28846 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
28847 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
28848 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
28850 <file name="protractor.js" type="protractor">
28851 it('should execute ng-click but not reload when href without value', function() {
28852 element(by.id('link-1')).click();
28853 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
28854 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
28857 it('should execute ng-click but not reload when href empty string', function() {
28858 element(by.id('link-2')).click();
28859 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
28860 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
28863 it('should execute ng-click and change url when ng-href specified', function() {
28864 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
28866 element(by.id('link-3')).click();
28868 // At this point, we navigate away from an Angular page, so we need
28869 // to use browser.driver to get the base webdriver.
28871 browser.wait(function() {
28872 return browser.driver.getCurrentUrl().then(function(url) {
28873 return url.match(/\/123$/);
28875 }, 5000, 'page should navigate to /123');
28878 it('should execute ng-click but not reload when href empty string and name specified', function() {
28879 element(by.id('link-4')).click();
28880 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
28881 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
28884 it('should execute ng-click but not reload when no href but name specified', function() {
28885 element(by.id('link-5')).click();
28886 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
28887 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
28890 it('should only change url when only ng-href', function() {
28891 element(by.model('value')).clear();
28892 element(by.model('value')).sendKeys('6');
28893 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
28895 element(by.id('link-6')).click();
28897 // At this point, we navigate away from an Angular page, so we need
28898 // to use browser.driver to get the base webdriver.
28899 browser.wait(function() {
28900 return browser.driver.getCurrentUrl().then(function(url) {
28901 return url.match(/\/6$/);
28903 }, 5000, 'page should navigate to /6');
28916 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
28917 * work right: The browser will fetch from the URL with the literal
28918 * text `{{hash}}` until Angular replaces the expression inside
28919 * `{{hash}}`. The `ngSrc` directive solves this problem.
28921 * The buggy way to write it:
28923 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
28926 * The correct way to write it:
28928 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
28932 * @param {template} ngSrc any string which can contain `{{}}` markup.
28942 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
28943 * work right: The browser will fetch from the URL with the literal
28944 * text `{{hash}}` until Angular replaces the expression inside
28945 * `{{hash}}`. The `ngSrcset` directive solves this problem.
28947 * The buggy way to write it:
28949 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
28952 * The correct way to write it:
28954 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
28958 * @param {template} ngSrcset any string which can contain `{{}}` markup.
28969 * This directive sets the `disabled` attribute on the element if the
28970 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
28972 * A special directive is necessary because we cannot use interpolation inside the `disabled`
28973 * attribute. The following example would make the button enabled on Chrome/Firefox
28974 * but not on older IEs:
28977 * <!-- See below for an example of ng-disabled being used correctly -->
28978 * <div ng-init="isDisabled = false">
28979 * <button disabled="{{isDisabled}}">Disabled</button>
28983 * This is because the HTML specification does not require browsers to preserve the values of
28984 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
28985 * If we put an Angular interpolation expression into such an attribute then the
28986 * binding information would be lost when the browser removes the attribute.
28990 <file name="index.html">
28991 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
28992 <button ng-model="button" ng-disabled="checked">Button</button>
28994 <file name="protractor.js" type="protractor">
28995 it('should toggle button', function() {
28996 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
28997 element(by.model('checked')).click();
28998 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
29004 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
29005 * then the `disabled` attribute will be set on the element
29016 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
29018 * Note that this directive should not be used together with {@link ngModel `ngModel`},
29019 * as this can lead to unexpected behavior.
29021 * ### Why do we need `ngChecked`?
29023 * The HTML specification does not require browsers to preserve the values of boolean attributes
29024 * such as checked. (Their presence means true and their absence means false.)
29025 * If we put an Angular interpolation expression into such an attribute then the
29026 * binding information would be lost when the browser removes the attribute.
29027 * The `ngChecked` directive solves this problem for the `checked` attribute.
29028 * This complementary directive is not removed by the browser and so provides
29029 * a permanent reliable place to store the binding information.
29032 <file name="index.html">
29033 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
29034 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
29036 <file name="protractor.js" type="protractor">
29037 it('should check both checkBoxes', function() {
29038 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
29039 element(by.model('master')).click();
29040 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
29046 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
29047 * then the `checked` attribute will be set on the element
29058 * The HTML specification does not require browsers to preserve the values of boolean attributes
29059 * such as readonly. (Their presence means true and their absence means false.)
29060 * If we put an Angular interpolation expression into such an attribute then the
29061 * binding information would be lost when the browser removes the attribute.
29062 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
29063 * This complementary directive is not removed by the browser and so provides
29064 * a permanent reliable place to store the binding information.
29067 <file name="index.html">
29068 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
29069 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
29071 <file name="protractor.js" type="protractor">
29072 it('should toggle readonly attr', function() {
29073 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
29074 element(by.model('checked')).click();
29075 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
29081 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
29082 * then special attribute "readonly" will be set on the element
29093 * The HTML specification does not require browsers to preserve the values of boolean attributes
29094 * such as selected. (Their presence means true and their absence means false.)
29095 * If we put an Angular interpolation expression into such an attribute then the
29096 * binding information would be lost when the browser removes the attribute.
29097 * The `ngSelected` directive solves this problem for the `selected` attribute.
29098 * This complementary directive is not removed by the browser and so provides
29099 * a permanent reliable place to store the binding information.
29103 <file name="index.html">
29104 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
29105 <select aria-label="ngSelected demo">
29106 <option>Hello!</option>
29107 <option id="greet" ng-selected="selected">Greetings!</option>
29110 <file name="protractor.js" type="protractor">
29111 it('should select Greetings!', function() {
29112 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
29113 element(by.model('selected')).click();
29114 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
29120 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
29121 * then special attribute "selected" will be set on the element
29131 * The HTML specification does not require browsers to preserve the values of boolean attributes
29132 * such as open. (Their presence means true and their absence means false.)
29133 * If we put an Angular interpolation expression into such an attribute then the
29134 * binding information would be lost when the browser removes the attribute.
29135 * The `ngOpen` directive solves this problem for the `open` attribute.
29136 * This complementary directive is not removed by the browser and so provides
29137 * a permanent reliable place to store the binding information.
29140 <file name="index.html">
29141 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
29142 <details id="details" ng-open="open">
29143 <summary>Show/Hide me</summary>
29146 <file name="protractor.js" type="protractor">
29147 it('should toggle open', function() {
29148 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
29149 element(by.model('open')).click();
29150 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
29156 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
29157 * then special attribute "open" will be set on the element
29160 var ngAttributeAliasDirectives = {};
29162 // boolean attrs are evaluated
29163 forEach(BOOLEAN_ATTR, function(propName, attrName) {
29164 // binding to multiple is not supported
29165 if (propName == "multiple") return;
29167 function defaultLinkFn(scope, element, attr) {
29168 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
29169 attr.$set(attrName, !!value);
29173 var normalized = directiveNormalize('ng-' + attrName);
29174 var linkFn = defaultLinkFn;
29176 if (propName === 'checked') {
29177 linkFn = function(scope, element, attr) {
29178 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
29179 if (attr.ngModel !== attr[normalized]) {
29180 defaultLinkFn(scope, element, attr);
29185 ngAttributeAliasDirectives[normalized] = function() {
29194 // aliased input attrs are evaluated
29195 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
29196 ngAttributeAliasDirectives[ngAttr] = function() {
29199 link: function(scope, element, attr) {
29200 //special case ngPattern when a literal regular expression value
29201 //is used as the expression (this way we don't have to watch anything).
29202 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
29203 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
29205 attr.$set("ngPattern", new RegExp(match[1], match[2]));
29210 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
29211 attr.$set(ngAttr, value);
29218 // ng-src, ng-srcset, ng-href are interpolated
29219 forEach(['src', 'srcset', 'href'], function(attrName) {
29220 var normalized = directiveNormalize('ng-' + attrName);
29221 ngAttributeAliasDirectives[normalized] = function() {
29223 priority: 99, // it needs to run after the attributes are interpolated
29224 link: function(scope, element, attr) {
29225 var propName = attrName,
29228 if (attrName === 'href' &&
29229 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
29230 name = 'xlinkHref';
29231 attr.$attr[name] = 'xlink:href';
29235 attr.$observe(normalized, function(value) {
29237 if (attrName === 'href') {
29238 attr.$set(name, null);
29243 attr.$set(name, value);
29245 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
29246 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
29247 // to set the property as well to achieve the desired effect.
29248 // we use attr[attrName] value since $set can sanitize the url.
29249 if (msie && propName) element.prop(propName, attr[name]);
29256 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
29258 var nullFormCtrl = {
29260 $$renameControl: nullFormRenameControl,
29261 $removeControl: noop,
29262 $setValidity: noop,
29264 $setPristine: noop,
29265 $setSubmitted: noop
29267 SUBMITTED_CLASS = 'ng-submitted';
29269 function nullFormRenameControl(control, name) {
29270 control.$name = name;
29275 * @name form.FormController
29277 * @property {boolean} $pristine True if user has not interacted with the form yet.
29278 * @property {boolean} $dirty True if user has already interacted with the form.
29279 * @property {boolean} $valid True if all of the containing forms and controls are valid.
29280 * @property {boolean} $invalid True if at least one containing control or form is invalid.
29281 * @property {boolean} $pending True if at least one containing control or form is pending.
29282 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
29284 * @property {Object} $error Is an object hash, containing references to controls or
29285 * forms with failing validators, where:
29287 * - keys are validation tokens (error names),
29288 * - values are arrays of controls or forms that have a failing validator for given error name.
29290 * Built-in validation tokens:
29302 * - `datetimelocal`
29308 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
29309 * such as being valid/invalid or dirty/pristine.
29311 * Each {@link ng.directive:form form} directive creates an instance
29312 * of `FormController`.
29315 //asks for $scope to fool the BC controller module
29316 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
29317 function FormController(element, attrs, $scope, $animate, $interpolate) {
29323 form.$$success = {};
29324 form.$pending = undefined;
29325 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
29326 form.$dirty = false;
29327 form.$pristine = true;
29328 form.$valid = true;
29329 form.$invalid = false;
29330 form.$submitted = false;
29331 form.$$parentForm = nullFormCtrl;
29335 * @name form.FormController#$rollbackViewValue
29338 * Rollback all form controls pending updates to the `$modelValue`.
29340 * Updates may be pending by a debounced event or because the input is waiting for a some future
29341 * event defined in `ng-model-options`. This method is typically needed by the reset button of
29342 * a form that uses `ng-model-options` to pend updates.
29344 form.$rollbackViewValue = function() {
29345 forEach(controls, function(control) {
29346 control.$rollbackViewValue();
29352 * @name form.FormController#$commitViewValue
29355 * Commit all form controls pending updates to the `$modelValue`.
29357 * Updates may be pending by a debounced event or because the input is waiting for a some future
29358 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
29359 * usually handles calling this in response to input events.
29361 form.$commitViewValue = function() {
29362 forEach(controls, function(control) {
29363 control.$commitViewValue();
29369 * @name form.FormController#$addControl
29370 * @param {object} control control object, either a {@link form.FormController} or an
29371 * {@link ngModel.NgModelController}
29374 * Register a control with the form. Input elements using ngModelController do this automatically
29375 * when they are linked.
29377 * Note that the current state of the control will not be reflected on the new parent form. This
29378 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
29381 * However, if the method is used programmatically, for example by adding dynamically created controls,
29382 * or controls that have been previously removed without destroying their corresponding DOM element,
29383 * it's the developers responsiblity to make sure the current state propagates to the parent form.
29385 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
29386 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
29388 form.$addControl = function(control) {
29389 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
29390 // and not added to the scope. Now we throw an error.
29391 assertNotHasOwnProperty(control.$name, 'input');
29392 controls.push(control);
29394 if (control.$name) {
29395 form[control.$name] = control;
29398 control.$$parentForm = form;
29401 // Private API: rename a form control
29402 form.$$renameControl = function(control, newName) {
29403 var oldName = control.$name;
29405 if (form[oldName] === control) {
29406 delete form[oldName];
29408 form[newName] = control;
29409 control.$name = newName;
29414 * @name form.FormController#$removeControl
29415 * @param {object} control control object, either a {@link form.FormController} or an
29416 * {@link ngModel.NgModelController}
29419 * Deregister a control from the form.
29421 * Input elements using ngModelController do this automatically when they are destroyed.
29423 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
29424 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
29425 * different from case to case. For example, removing the only `$dirty` control from a form may or
29426 * may not mean that the form is still `$dirty`.
29428 form.$removeControl = function(control) {
29429 if (control.$name && form[control.$name] === control) {
29430 delete form[control.$name];
29432 forEach(form.$pending, function(value, name) {
29433 form.$setValidity(name, null, control);
29435 forEach(form.$error, function(value, name) {
29436 form.$setValidity(name, null, control);
29438 forEach(form.$$success, function(value, name) {
29439 form.$setValidity(name, null, control);
29442 arrayRemove(controls, control);
29443 control.$$parentForm = nullFormCtrl;
29449 * @name form.FormController#$setValidity
29452 * Sets the validity of a form control.
29454 * This method will also propagate to parent forms.
29456 addSetValidityMethod({
29459 set: function(object, property, controller) {
29460 var list = object[property];
29462 object[property] = [controller];
29464 var index = list.indexOf(controller);
29465 if (index === -1) {
29466 list.push(controller);
29470 unset: function(object, property, controller) {
29471 var list = object[property];
29475 arrayRemove(list, controller);
29476 if (list.length === 0) {
29477 delete object[property];
29485 * @name form.FormController#$setDirty
29488 * Sets the form to a dirty state.
29490 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
29491 * state (ng-dirty class). This method will also propagate to parent forms.
29493 form.$setDirty = function() {
29494 $animate.removeClass(element, PRISTINE_CLASS);
29495 $animate.addClass(element, DIRTY_CLASS);
29496 form.$dirty = true;
29497 form.$pristine = false;
29498 form.$$parentForm.$setDirty();
29503 * @name form.FormController#$setPristine
29506 * Sets the form to its pristine state.
29508 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
29509 * state (ng-pristine class). This method will also propagate to all the controls contained
29512 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
29513 * saving or resetting it.
29515 form.$setPristine = function() {
29516 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
29517 form.$dirty = false;
29518 form.$pristine = true;
29519 form.$submitted = false;
29520 forEach(controls, function(control) {
29521 control.$setPristine();
29527 * @name form.FormController#$setUntouched
29530 * Sets the form to its untouched state.
29532 * This method can be called to remove the 'ng-touched' class and set the form controls to their
29533 * untouched state (ng-untouched class).
29535 * Setting a form controls back to their untouched state is often useful when setting the form
29536 * back to its pristine state.
29538 form.$setUntouched = function() {
29539 forEach(controls, function(control) {
29540 control.$setUntouched();
29546 * @name form.FormController#$setSubmitted
29549 * Sets the form to its submitted state.
29551 form.$setSubmitted = function() {
29552 $animate.addClass(element, SUBMITTED_CLASS);
29553 form.$submitted = true;
29554 form.$$parentForm.$setSubmitted();
29564 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
29565 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
29566 * sub-group of controls needs to be determined.
29568 * Note: the purpose of `ngForm` is to group controls,
29569 * but not to be a replacement for the `<form>` tag with all of its capabilities
29570 * (e.g. posting to the server, ...).
29572 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
29573 * related scope, under this name.
29583 * Directive that instantiates
29584 * {@link form.FormController FormController}.
29586 * If the `name` attribute is specified, the form controller is published onto the current scope under
29589 * # Alias: {@link ng.directive:ngForm `ngForm`}
29591 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
29592 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
29593 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
29594 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
29595 * using Angular validation directives in forms that are dynamically generated using the
29596 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
29597 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
29598 * `ngForm` directive and nest these in an outer `form` element.
29602 * - `ng-valid` is set if the form is valid.
29603 * - `ng-invalid` is set if the form is invalid.
29604 * - `ng-pending` is set if the form is pending.
29605 * - `ng-pristine` is set if the form is pristine.
29606 * - `ng-dirty` is set if the form is dirty.
29607 * - `ng-submitted` is set if the form was submitted.
29609 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
29612 * # Submitting a form and preventing the default action
29614 * Since the role of forms in client-side Angular applications is different than in classical
29615 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
29616 * page reload that sends the data to the server. Instead some javascript logic should be triggered
29617 * to handle the form submission in an application-specific way.
29619 * For this reason, Angular prevents the default action (form submission to the server) unless the
29620 * `<form>` element has an `action` attribute specified.
29622 * You can use one of the following two ways to specify what javascript method should be called when
29623 * a form is submitted:
29625 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
29626 * - {@link ng.directive:ngClick ngClick} directive on the first
29627 * button or input field of type submit (input[type=submit])
29629 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
29630 * or {@link ng.directive:ngClick ngClick} directives.
29631 * This is because of the following form submission rules in the HTML specification:
29633 * - If a form has only one input field then hitting enter in this field triggers form submit
29635 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
29636 * doesn't trigger submit
29637 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
29638 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
29639 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
29641 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
29642 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
29643 * to have access to the updated model.
29645 * ## Animation Hooks
29647 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
29648 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
29649 * other validations that are performed within the form. Animations in ngForm are similar to how
29650 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
29651 * as JS animations.
29653 * The following example shows a simple way to utilize CSS transitions to style a form element
29654 * that has been rendered as invalid after it has been validated:
29657 * //be sure to include ngAnimate as a module to hook into more
29658 * //advanced animations
29660 * transition:0.5s linear all;
29661 * background: white;
29663 * .my-form.ng-invalid {
29670 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
29671 <file name="index.html">
29673 angular.module('formExample', [])
29674 .controller('FormController', ['$scope', function($scope) {
29675 $scope.userType = 'guest';
29680 transition:all linear 0.5s;
29681 background: transparent;
29683 .my-form.ng-invalid {
29687 <form name="myForm" ng-controller="FormController" class="my-form">
29688 userType: <input name="input" ng-model="userType" required>
29689 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
29690 <code>userType = {{userType}}</code><br>
29691 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
29692 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
29693 <code>myForm.$valid = {{myForm.$valid}}</code><br>
29694 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
29697 <file name="protractor.js" type="protractor">
29698 it('should initialize to model', function() {
29699 var userType = element(by.binding('userType'));
29700 var valid = element(by.binding('myForm.input.$valid'));
29702 expect(userType.getText()).toContain('guest');
29703 expect(valid.getText()).toContain('true');
29706 it('should be invalid if empty', function() {
29707 var userType = element(by.binding('userType'));
29708 var valid = element(by.binding('myForm.input.$valid'));
29709 var userInput = element(by.model('userType'));
29712 userInput.sendKeys('');
29714 expect(userType.getText()).toEqual('userType =');
29715 expect(valid.getText()).toContain('false');
29720 * @param {string=} name Name of the form. If specified, the form controller will be published into
29721 * related scope, under this name.
29723 var formDirectiveFactory = function(isNgForm) {
29724 return ['$timeout', '$parse', function($timeout, $parse) {
29725 var formDirective = {
29727 restrict: isNgForm ? 'EAC' : 'E',
29728 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
29729 controller: FormController,
29730 compile: function ngFormCompile(formElement, attr) {
29731 // Setup initial state of the control
29732 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
29734 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
29737 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
29738 var controller = ctrls[0];
29740 // if `action` attr is not present on the form, prevent the default action (submission)
29741 if (!('action' in attr)) {
29742 // we can't use jq events because if a form is destroyed during submission the default
29743 // action is not prevented. see #1238
29745 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
29746 // page reload if the form was destroyed by submission of the form via a click handler
29747 // on a button in the form. Looks like an IE9 specific bug.
29748 var handleFormSubmission = function(event) {
29749 scope.$apply(function() {
29750 controller.$commitViewValue();
29751 controller.$setSubmitted();
29754 event.preventDefault();
29757 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
29759 // unregister the preventDefault listener so that we don't not leak memory but in a
29760 // way that will achieve the prevention of the default action.
29761 formElement.on('$destroy', function() {
29762 $timeout(function() {
29763 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
29768 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
29769 parentFormCtrl.$addControl(controller);
29771 var setter = nameAttr ? getSetter(controller.$name) : noop;
29774 setter(scope, controller);
29775 attr.$observe(nameAttr, function(newValue) {
29776 if (controller.$name === newValue) return;
29777 setter(scope, undefined);
29778 controller.$$parentForm.$$renameControl(controller, newValue);
29779 setter = getSetter(controller.$name);
29780 setter(scope, controller);
29783 formElement.on('$destroy', function() {
29784 controller.$$parentForm.$removeControl(controller);
29785 setter(scope, undefined);
29786 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
29793 return formDirective;
29795 function getSetter(expression) {
29796 if (expression === '') {
29797 //create an assignable expression, so forms with an empty name can be renamed later
29798 return $parse('this[""]').assign;
29800 return $parse(expression).assign || noop;
29805 var formDirective = formDirectiveFactory();
29806 var ngFormDirective = formDirectiveFactory(true);
29808 /* global VALID_CLASS: false,
29809 INVALID_CLASS: false,
29810 PRISTINE_CLASS: false,
29811 DIRTY_CLASS: false,
29812 UNTOUCHED_CLASS: false,
29813 TOUCHED_CLASS: false,
29814 ngModelMinErr: false,
29817 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
29818 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)/;
29819 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
29820 var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
29821 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;
29822 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
29823 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
29824 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
29825 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
29826 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
29827 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
29833 * @name input[text]
29836 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
29839 * @param {string} ngModel Assignable angular expression to data-bind to.
29840 * @param {string=} name Property name of the form under which the control is published.
29841 * @param {string=} required Adds `required` validation error key if the value is not entered.
29842 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29843 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29844 * `required` when you want to data-bind to the `required` attribute.
29845 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
29847 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
29848 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
29850 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
29851 * that contains the regular expression body that will be converted to a regular expression
29852 * as in the ngPattern directive.
29853 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
29854 * a RegExp found by evaluating the Angular expression given in the attribute value.
29855 * If the expression evaluates to a RegExp object, then this is used directly.
29856 * If the expression evaluates to a string, then it will be converted to a RegExp
29857 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
29858 * `new RegExp('^abc$')`.<br />
29859 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29860 * start at the index of the last search's match, thus not taking the whole input value into
29862 * @param {string=} ngChange Angular expression to be executed when input changes due to user
29863 * interaction with the input element.
29864 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
29865 * This parameter is ignored for input[type=password] controls, which will never trim the
29869 <example name="text-input-directive" module="textInputExample">
29870 <file name="index.html">
29872 angular.module('textInputExample', [])
29873 .controller('ExampleController', ['$scope', function($scope) {
29876 word: /^\s*\w*\s*$/
29880 <form name="myForm" ng-controller="ExampleController">
29881 <label>Single word:
29882 <input type="text" name="input" ng-model="example.text"
29883 ng-pattern="example.word" required ng-trim="false">
29886 <span class="error" ng-show="myForm.input.$error.required">
29888 <span class="error" ng-show="myForm.input.$error.pattern">
29889 Single word only!</span>
29891 <tt>text = {{example.text}}</tt><br/>
29892 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29893 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29894 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29895 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29898 <file name="protractor.js" type="protractor">
29899 var text = element(by.binding('example.text'));
29900 var valid = element(by.binding('myForm.input.$valid'));
29901 var input = element(by.model('example.text'));
29903 it('should initialize to model', function() {
29904 expect(text.getText()).toContain('guest');
29905 expect(valid.getText()).toContain('true');
29908 it('should be invalid if empty', function() {
29910 input.sendKeys('');
29912 expect(text.getText()).toEqual('text =');
29913 expect(valid.getText()).toContain('false');
29916 it('should be invalid if multi word', function() {
29918 input.sendKeys('hello world');
29920 expect(valid.getText()).toContain('false');
29925 'text': textInputType,
29929 * @name input[date]
29932 * Input with date validation and transformation. In browsers that do not yet support
29933 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
29934 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
29935 * modern browsers do not yet support this input type, it is important to provide cues to users on the
29936 * expected input format via a placeholder or label.
29938 * The model must always be a Date object, otherwise Angular will throw an error.
29939 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
29941 * The timezone to be used to read/write the `Date` instance in the model can be defined using
29942 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
29944 * @param {string} ngModel Assignable angular expression to data-bind to.
29945 * @param {string=} name Property name of the form under which the control is published.
29946 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
29947 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
29948 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
29949 * constraint validation.
29950 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
29951 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
29952 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
29953 * constraint validation.
29954 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
29955 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
29956 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
29957 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
29958 * @param {string=} required Sets `required` validation error key if the value is not entered.
29959 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29960 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29961 * `required` when you want to data-bind to the `required` attribute.
29962 * @param {string=} ngChange Angular expression to be executed when input changes due to user
29963 * interaction with the input element.
29966 <example name="date-input-directive" module="dateInputExample">
29967 <file name="index.html">
29969 angular.module('dateInputExample', [])
29970 .controller('DateController', ['$scope', function($scope) {
29972 value: new Date(2013, 9, 22)
29976 <form name="myForm" ng-controller="DateController as dateCtrl">
29977 <label for="exampleInput">Pick a date in 2013:</label>
29978 <input type="date" id="exampleInput" name="input" ng-model="example.value"
29979 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
29981 <span class="error" ng-show="myForm.input.$error.required">
29983 <span class="error" ng-show="myForm.input.$error.date">
29984 Not a valid date!</span>
29986 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
29987 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29988 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29989 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29990 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29993 <file name="protractor.js" type="protractor">
29994 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
29995 var valid = element(by.binding('myForm.input.$valid'));
29996 var input = element(by.model('example.value'));
29998 // currently protractor/webdriver does not support
29999 // sending keys to all known HTML5 input controls
30000 // for various browsers (see https://github.com/angular/protractor/issues/562).
30001 function setInput(val) {
30002 // set the value of the element and force validation.
30003 var scr = "var ipt = document.getElementById('exampleInput'); " +
30004 "ipt.value = '" + val + "';" +
30005 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
30006 browser.executeScript(scr);
30009 it('should initialize to model', function() {
30010 expect(value.getText()).toContain('2013-10-22');
30011 expect(valid.getText()).toContain('myForm.input.$valid = true');
30014 it('should be invalid if empty', function() {
30016 expect(value.getText()).toEqual('value =');
30017 expect(valid.getText()).toContain('myForm.input.$valid = false');
30020 it('should be invalid if over max', function() {
30021 setInput('2015-01-01');
30022 expect(value.getText()).toContain('');
30023 expect(valid.getText()).toContain('myForm.input.$valid = false');
30028 'date': createDateInputType('date', DATE_REGEXP,
30029 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
30034 * @name input[datetime-local]
30037 * Input with datetime validation and transformation. In browsers that do not yet support
30038 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
30039 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
30041 * The model must always be a Date object, otherwise Angular will throw an error.
30042 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
30044 * The timezone to be used to read/write the `Date` instance in the model can be defined using
30045 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
30047 * @param {string} ngModel Assignable angular expression to data-bind to.
30048 * @param {string=} name Property name of the form under which the control is published.
30049 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
30050 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
30051 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
30052 * Note that `min` will also add native HTML5 constraint validation.
30053 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
30054 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
30055 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
30056 * Note that `max` will also add native HTML5 constraint validation.
30057 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
30058 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
30059 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
30060 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
30061 * @param {string=} required Sets `required` validation error key if the value is not entered.
30062 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30063 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30064 * `required` when you want to data-bind to the `required` attribute.
30065 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30066 * interaction with the input element.
30069 <example name="datetimelocal-input-directive" module="dateExample">
30070 <file name="index.html">
30072 angular.module('dateExample', [])
30073 .controller('DateController', ['$scope', function($scope) {
30075 value: new Date(2010, 11, 28, 14, 57)
30079 <form name="myForm" ng-controller="DateController as dateCtrl">
30080 <label for="exampleInput">Pick a date between in 2013:</label>
30081 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
30082 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
30084 <span class="error" ng-show="myForm.input.$error.required">
30086 <span class="error" ng-show="myForm.input.$error.datetimelocal">
30087 Not a valid date!</span>
30089 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
30090 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30091 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30092 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30093 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30096 <file name="protractor.js" type="protractor">
30097 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
30098 var valid = element(by.binding('myForm.input.$valid'));
30099 var input = element(by.model('example.value'));
30101 // currently protractor/webdriver does not support
30102 // sending keys to all known HTML5 input controls
30103 // for various browsers (https://github.com/angular/protractor/issues/562).
30104 function setInput(val) {
30105 // set the value of the element and force validation.
30106 var scr = "var ipt = document.getElementById('exampleInput'); " +
30107 "ipt.value = '" + val + "';" +
30108 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
30109 browser.executeScript(scr);
30112 it('should initialize to model', function() {
30113 expect(value.getText()).toContain('2010-12-28T14:57:00');
30114 expect(valid.getText()).toContain('myForm.input.$valid = true');
30117 it('should be invalid if empty', function() {
30119 expect(value.getText()).toEqual('value =');
30120 expect(valid.getText()).toContain('myForm.input.$valid = false');
30123 it('should be invalid if over max', function() {
30124 setInput('2015-01-01T23:59:00');
30125 expect(value.getText()).toContain('');
30126 expect(valid.getText()).toContain('myForm.input.$valid = false');
30131 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
30132 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
30133 'yyyy-MM-ddTHH:mm:ss.sss'),
30137 * @name input[time]
30140 * Input with time validation and transformation. In browsers that do not yet support
30141 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
30142 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
30143 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
30145 * The model must always be a Date object, otherwise Angular will throw an error.
30146 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
30148 * The timezone to be used to read/write the `Date` instance in the model can be defined using
30149 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
30151 * @param {string} ngModel Assignable angular expression to data-bind to.
30152 * @param {string=} name Property name of the form under which the control is published.
30153 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
30154 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
30155 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
30156 * native HTML5 constraint validation.
30157 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
30158 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
30159 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
30160 * native HTML5 constraint validation.
30161 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
30162 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
30163 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
30164 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
30165 * @param {string=} required Sets `required` validation error key if the value is not entered.
30166 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30167 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30168 * `required` when you want to data-bind to the `required` attribute.
30169 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30170 * interaction with the input element.
30173 <example name="time-input-directive" module="timeExample">
30174 <file name="index.html">
30176 angular.module('timeExample', [])
30177 .controller('DateController', ['$scope', function($scope) {
30179 value: new Date(1970, 0, 1, 14, 57, 0)
30183 <form name="myForm" ng-controller="DateController as dateCtrl">
30184 <label for="exampleInput">Pick a between 8am and 5pm:</label>
30185 <input type="time" id="exampleInput" name="input" ng-model="example.value"
30186 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
30188 <span class="error" ng-show="myForm.input.$error.required">
30190 <span class="error" ng-show="myForm.input.$error.time">
30191 Not a valid date!</span>
30193 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
30194 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30195 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30196 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30197 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30200 <file name="protractor.js" type="protractor">
30201 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
30202 var valid = element(by.binding('myForm.input.$valid'));
30203 var input = element(by.model('example.value'));
30205 // currently protractor/webdriver does not support
30206 // sending keys to all known HTML5 input controls
30207 // for various browsers (https://github.com/angular/protractor/issues/562).
30208 function setInput(val) {
30209 // set the value of the element and force validation.
30210 var scr = "var ipt = document.getElementById('exampleInput'); " +
30211 "ipt.value = '" + val + "';" +
30212 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
30213 browser.executeScript(scr);
30216 it('should initialize to model', function() {
30217 expect(value.getText()).toContain('14:57:00');
30218 expect(valid.getText()).toContain('myForm.input.$valid = true');
30221 it('should be invalid if empty', function() {
30223 expect(value.getText()).toEqual('value =');
30224 expect(valid.getText()).toContain('myForm.input.$valid = false');
30227 it('should be invalid if over max', function() {
30228 setInput('23:59:00');
30229 expect(value.getText()).toContain('');
30230 expect(valid.getText()).toContain('myForm.input.$valid = false');
30235 'time': createDateInputType('time', TIME_REGEXP,
30236 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
30241 * @name input[week]
30244 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
30245 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
30246 * week format (yyyy-W##), for example: `2013-W02`.
30248 * The model must always be a Date object, otherwise Angular will throw an error.
30249 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
30251 * The timezone to be used to read/write the `Date` instance in the model can be defined using
30252 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
30254 * @param {string} ngModel Assignable angular expression to data-bind to.
30255 * @param {string=} name Property name of the form under which the control is published.
30256 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
30257 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
30258 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
30259 * native HTML5 constraint validation.
30260 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
30261 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
30262 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
30263 * native HTML5 constraint validation.
30264 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
30265 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
30266 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
30267 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
30268 * @param {string=} required Sets `required` validation error key if the value is not entered.
30269 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30270 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30271 * `required` when you want to data-bind to the `required` attribute.
30272 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30273 * interaction with the input element.
30276 <example name="week-input-directive" module="weekExample">
30277 <file name="index.html">
30279 angular.module('weekExample', [])
30280 .controller('DateController', ['$scope', function($scope) {
30282 value: new Date(2013, 0, 3)
30286 <form name="myForm" ng-controller="DateController as dateCtrl">
30287 <label>Pick a date between in 2013:
30288 <input id="exampleInput" type="week" name="input" ng-model="example.value"
30289 placeholder="YYYY-W##" min="2012-W32"
30290 max="2013-W52" required />
30293 <span class="error" ng-show="myForm.input.$error.required">
30295 <span class="error" ng-show="myForm.input.$error.week">
30296 Not a valid date!</span>
30298 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
30299 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30300 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30301 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30302 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30305 <file name="protractor.js" type="protractor">
30306 var value = element(by.binding('example.value | date: "yyyy-Www"'));
30307 var valid = element(by.binding('myForm.input.$valid'));
30308 var input = element(by.model('example.value'));
30310 // currently protractor/webdriver does not support
30311 // sending keys to all known HTML5 input controls
30312 // for various browsers (https://github.com/angular/protractor/issues/562).
30313 function setInput(val) {
30314 // set the value of the element and force validation.
30315 var scr = "var ipt = document.getElementById('exampleInput'); " +
30316 "ipt.value = '" + val + "';" +
30317 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
30318 browser.executeScript(scr);
30321 it('should initialize to model', function() {
30322 expect(value.getText()).toContain('2013-W01');
30323 expect(valid.getText()).toContain('myForm.input.$valid = true');
30326 it('should be invalid if empty', function() {
30328 expect(value.getText()).toEqual('value =');
30329 expect(valid.getText()).toContain('myForm.input.$valid = false');
30332 it('should be invalid if over max', function() {
30333 setInput('2015-W01');
30334 expect(value.getText()).toContain('');
30335 expect(valid.getText()).toContain('myForm.input.$valid = false');
30340 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
30344 * @name input[month]
30347 * Input with month validation and transformation. In browsers that do not yet support
30348 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
30349 * month format (yyyy-MM), for example: `2009-01`.
30351 * The model must always be a Date object, otherwise Angular will throw an error.
30352 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
30353 * If the model is not set to the first of the month, the next view to model update will set it
30354 * to the first of the month.
30356 * The timezone to be used to read/write the `Date` instance in the model can be defined using
30357 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
30359 * @param {string} ngModel Assignable angular expression to data-bind to.
30360 * @param {string=} name Property name of the form under which the control is published.
30361 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
30362 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
30363 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
30364 * native HTML5 constraint validation.
30365 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
30366 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
30367 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
30368 * native HTML5 constraint validation.
30369 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
30370 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
30371 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
30372 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
30374 * @param {string=} required Sets `required` validation error key if the value is not entered.
30375 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30376 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30377 * `required` when you want to data-bind to the `required` attribute.
30378 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30379 * interaction with the input element.
30382 <example name="month-input-directive" module="monthExample">
30383 <file name="index.html">
30385 angular.module('monthExample', [])
30386 .controller('DateController', ['$scope', function($scope) {
30388 value: new Date(2013, 9, 1)
30392 <form name="myForm" ng-controller="DateController as dateCtrl">
30393 <label for="exampleInput">Pick a month in 2013:</label>
30394 <input id="exampleInput" type="month" name="input" ng-model="example.value"
30395 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
30397 <span class="error" ng-show="myForm.input.$error.required">
30399 <span class="error" ng-show="myForm.input.$error.month">
30400 Not a valid month!</span>
30402 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
30403 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30404 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30405 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30406 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30409 <file name="protractor.js" type="protractor">
30410 var value = element(by.binding('example.value | date: "yyyy-MM"'));
30411 var valid = element(by.binding('myForm.input.$valid'));
30412 var input = element(by.model('example.value'));
30414 // currently protractor/webdriver does not support
30415 // sending keys to all known HTML5 input controls
30416 // for various browsers (https://github.com/angular/protractor/issues/562).
30417 function setInput(val) {
30418 // set the value of the element and force validation.
30419 var scr = "var ipt = document.getElementById('exampleInput'); " +
30420 "ipt.value = '" + val + "';" +
30421 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
30422 browser.executeScript(scr);
30425 it('should initialize to model', function() {
30426 expect(value.getText()).toContain('2013-10');
30427 expect(valid.getText()).toContain('myForm.input.$valid = true');
30430 it('should be invalid if empty', function() {
30432 expect(value.getText()).toEqual('value =');
30433 expect(valid.getText()).toContain('myForm.input.$valid = false');
30436 it('should be invalid if over max', function() {
30437 setInput('2015-01');
30438 expect(value.getText()).toContain('');
30439 expect(valid.getText()).toContain('myForm.input.$valid = false');
30444 'month': createDateInputType('month', MONTH_REGEXP,
30445 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
30450 * @name input[number]
30453 * Text input with number validation and transformation. Sets the `number` validation
30454 * error if not a valid number.
30456 * <div class="alert alert-warning">
30457 * The model must always be of type `number` otherwise Angular will throw an error.
30458 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
30459 * error docs for more information and an example of how to convert your model if necessary.
30462 * ## Issues with HTML5 constraint validation
30464 * In browsers that follow the
30465 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
30466 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
30467 * If a non-number is entered in the input, the browser will report the value as an empty string,
30468 * which means the view / model values in `ngModel` and subsequently the scope value
30469 * will also be an empty string.
30472 * @param {string} ngModel Assignable angular expression to data-bind to.
30473 * @param {string=} name Property name of the form under which the control is published.
30474 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
30475 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
30476 * @param {string=} required Sets `required` validation error key if the value is not entered.
30477 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30478 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30479 * `required` when you want to data-bind to the `required` attribute.
30480 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
30482 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
30483 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
30485 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
30486 * that contains the regular expression body that will be converted to a regular expression
30487 * as in the ngPattern directive.
30488 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
30489 * a RegExp found by evaluating the Angular expression given in the attribute value.
30490 * If the expression evaluates to a RegExp object, then this is used directly.
30491 * If the expression evaluates to a string, then it will be converted to a RegExp
30492 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
30493 * `new RegExp('^abc$')`.<br />
30494 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30495 * start at the index of the last search's match, thus not taking the whole input value into
30497 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30498 * interaction with the input element.
30501 <example name="number-input-directive" module="numberExample">
30502 <file name="index.html">
30504 angular.module('numberExample', [])
30505 .controller('ExampleController', ['$scope', function($scope) {
30511 <form name="myForm" ng-controller="ExampleController">
30513 <input type="number" name="input" ng-model="example.value"
30514 min="0" max="99" required>
30517 <span class="error" ng-show="myForm.input.$error.required">
30519 <span class="error" ng-show="myForm.input.$error.number">
30520 Not valid number!</span>
30522 <tt>value = {{example.value}}</tt><br/>
30523 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30524 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30525 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30526 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30529 <file name="protractor.js" type="protractor">
30530 var value = element(by.binding('example.value'));
30531 var valid = element(by.binding('myForm.input.$valid'));
30532 var input = element(by.model('example.value'));
30534 it('should initialize to model', function() {
30535 expect(value.getText()).toContain('12');
30536 expect(valid.getText()).toContain('true');
30539 it('should be invalid if empty', function() {
30541 input.sendKeys('');
30542 expect(value.getText()).toEqual('value =');
30543 expect(valid.getText()).toContain('false');
30546 it('should be invalid if over max', function() {
30548 input.sendKeys('123');
30549 expect(value.getText()).toEqual('value =');
30550 expect(valid.getText()).toContain('false');
30555 'number': numberInputType,
30563 * Text input with URL validation. Sets the `url` validation error key if the content is not a
30566 * <div class="alert alert-warning">
30567 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
30568 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
30569 * the built-in validators (see the {@link guide/forms Forms guide})
30572 * @param {string} ngModel Assignable angular expression to data-bind to.
30573 * @param {string=} name Property name of the form under which the control is published.
30574 * @param {string=} required Sets `required` validation error key if the value is not entered.
30575 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30576 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30577 * `required` when you want to data-bind to the `required` attribute.
30578 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
30580 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
30581 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
30583 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
30584 * that contains the regular expression body that will be converted to a regular expression
30585 * as in the ngPattern directive.
30586 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
30587 * a RegExp found by evaluating the Angular expression given in the attribute value.
30588 * If the expression evaluates to a RegExp object, then this is used directly.
30589 * If the expression evaluates to a string, then it will be converted to a RegExp
30590 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
30591 * `new RegExp('^abc$')`.<br />
30592 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30593 * start at the index of the last search's match, thus not taking the whole input value into
30595 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30596 * interaction with the input element.
30599 <example name="url-input-directive" module="urlExample">
30600 <file name="index.html">
30602 angular.module('urlExample', [])
30603 .controller('ExampleController', ['$scope', function($scope) {
30605 text: 'http://google.com'
30609 <form name="myForm" ng-controller="ExampleController">
30611 <input type="url" name="input" ng-model="url.text" required>
30614 <span class="error" ng-show="myForm.input.$error.required">
30616 <span class="error" ng-show="myForm.input.$error.url">
30617 Not valid url!</span>
30619 <tt>text = {{url.text}}</tt><br/>
30620 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30621 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30622 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30623 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30624 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
30627 <file name="protractor.js" type="protractor">
30628 var text = element(by.binding('url.text'));
30629 var valid = element(by.binding('myForm.input.$valid'));
30630 var input = element(by.model('url.text'));
30632 it('should initialize to model', function() {
30633 expect(text.getText()).toContain('http://google.com');
30634 expect(valid.getText()).toContain('true');
30637 it('should be invalid if empty', function() {
30639 input.sendKeys('');
30641 expect(text.getText()).toEqual('text =');
30642 expect(valid.getText()).toContain('false');
30645 it('should be invalid if not url', function() {
30647 input.sendKeys('box');
30649 expect(valid.getText()).toContain('false');
30654 'url': urlInputType,
30659 * @name input[email]
30662 * Text input with email validation. Sets the `email` validation error key if not a valid email
30665 * <div class="alert alert-warning">
30666 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
30667 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
30668 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
30671 * @param {string} ngModel Assignable angular expression to data-bind to.
30672 * @param {string=} name Property name of the form under which the control is published.
30673 * @param {string=} required Sets `required` validation error key if the value is not entered.
30674 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30675 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30676 * `required` when you want to data-bind to the `required` attribute.
30677 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
30679 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
30680 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
30682 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
30683 * that contains the regular expression body that will be converted to a regular expression
30684 * as in the ngPattern directive.
30685 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
30686 * a RegExp found by evaluating the Angular expression given in the attribute value.
30687 * If the expression evaluates to a RegExp object, then this is used directly.
30688 * If the expression evaluates to a string, then it will be converted to a RegExp
30689 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
30690 * `new RegExp('^abc$')`.<br />
30691 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30692 * start at the index of the last search's match, thus not taking the whole input value into
30694 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30695 * interaction with the input element.
30698 <example name="email-input-directive" module="emailExample">
30699 <file name="index.html">
30701 angular.module('emailExample', [])
30702 .controller('ExampleController', ['$scope', function($scope) {
30704 text: 'me@example.com'
30708 <form name="myForm" ng-controller="ExampleController">
30710 <input type="email" name="input" ng-model="email.text" required>
30713 <span class="error" ng-show="myForm.input.$error.required">
30715 <span class="error" ng-show="myForm.input.$error.email">
30716 Not valid email!</span>
30718 <tt>text = {{email.text}}</tt><br/>
30719 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30720 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30721 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30722 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30723 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
30726 <file name="protractor.js" type="protractor">
30727 var text = element(by.binding('email.text'));
30728 var valid = element(by.binding('myForm.input.$valid'));
30729 var input = element(by.model('email.text'));
30731 it('should initialize to model', function() {
30732 expect(text.getText()).toContain('me@example.com');
30733 expect(valid.getText()).toContain('true');
30736 it('should be invalid if empty', function() {
30738 input.sendKeys('');
30739 expect(text.getText()).toEqual('text =');
30740 expect(valid.getText()).toContain('false');
30743 it('should be invalid if not email', function() {
30745 input.sendKeys('xxx');
30747 expect(valid.getText()).toContain('false');
30752 'email': emailInputType,
30757 * @name input[radio]
30760 * HTML radio button.
30762 * @param {string} ngModel Assignable angular expression to data-bind to.
30763 * @param {string} value The value to which the `ngModel` expression should be set when selected.
30764 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
30765 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
30766 * @param {string=} name Property name of the form under which the control is published.
30767 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30768 * interaction with the input element.
30769 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
30770 * is selected. Should be used instead of the `value` attribute if you need
30771 * a non-string `ngModel` (`boolean`, `array`, ...).
30774 <example name="radio-input-directive" module="radioExample">
30775 <file name="index.html">
30777 angular.module('radioExample', [])
30778 .controller('ExampleController', ['$scope', function($scope) {
30782 $scope.specialValue = {
30788 <form name="myForm" ng-controller="ExampleController">
30790 <input type="radio" ng-model="color.name" value="red">
30794 <input type="radio" ng-model="color.name" ng-value="specialValue">
30798 <input type="radio" ng-model="color.name" value="blue">
30801 <tt>color = {{color.name | json}}</tt><br/>
30803 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
30805 <file name="protractor.js" type="protractor">
30806 it('should change state', function() {
30807 var color = element(by.binding('color.name'));
30809 expect(color.getText()).toContain('blue');
30811 element.all(by.model('color.name')).get(0).click();
30813 expect(color.getText()).toContain('red');
30818 'radio': radioInputType,
30823 * @name input[checkbox]
30828 * @param {string} ngModel Assignable angular expression to data-bind to.
30829 * @param {string=} name Property name of the form under which the control is published.
30830 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
30831 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
30832 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30833 * interaction with the input element.
30836 <example name="checkbox-input-directive" module="checkboxExample">
30837 <file name="index.html">
30839 angular.module('checkboxExample', [])
30840 .controller('ExampleController', ['$scope', function($scope) {
30841 $scope.checkboxModel = {
30847 <form name="myForm" ng-controller="ExampleController">
30849 <input type="checkbox" ng-model="checkboxModel.value1">
30852 <input type="checkbox" ng-model="checkboxModel.value2"
30853 ng-true-value="'YES'" ng-false-value="'NO'">
30855 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
30856 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
30859 <file name="protractor.js" type="protractor">
30860 it('should change state', function() {
30861 var value1 = element(by.binding('checkboxModel.value1'));
30862 var value2 = element(by.binding('checkboxModel.value2'));
30864 expect(value1.getText()).toContain('true');
30865 expect(value2.getText()).toContain('YES');
30867 element(by.model('checkboxModel.value1')).click();
30868 element(by.model('checkboxModel.value2')).click();
30870 expect(value1.getText()).toContain('false');
30871 expect(value2.getText()).toContain('NO');
30876 'checkbox': checkboxInputType,
30885 function stringBasedInputType(ctrl) {
30886 ctrl.$formatters.push(function(value) {
30887 return ctrl.$isEmpty(value) ? value : value.toString();
30891 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30892 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
30893 stringBasedInputType(ctrl);
30896 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30897 var type = lowercase(element[0].type);
30899 // In composition mode, users are still inputing intermediate text buffer,
30900 // hold the listener until composition is done.
30901 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
30902 if (!$sniffer.android) {
30903 var composing = false;
30905 element.on('compositionstart', function(data) {
30909 element.on('compositionend', function() {
30915 var listener = function(ev) {
30917 $browser.defer.cancel(timeout);
30920 if (composing) return;
30921 var value = element.val(),
30922 event = ev && ev.type;
30924 // By default we will trim the value
30925 // If the attribute ng-trim exists we will avoid trimming
30926 // If input type is 'password', the value is never trimmed
30927 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
30928 value = trim(value);
30931 // If a control is suffering from bad input (due to native validators), browsers discard its
30932 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
30933 // control's value is the same empty value twice in a row.
30934 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
30935 ctrl.$setViewValue(value, event);
30939 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
30940 // input event on backspace, delete or cut
30941 if ($sniffer.hasEvent('input')) {
30942 element.on('input', listener);
30946 var deferListener = function(ev, input, origValue) {
30948 timeout = $browser.defer(function() {
30950 if (!input || input.value !== origValue) {
30957 element.on('keydown', function(event) {
30958 var key = event.keyCode;
30961 // command modifiers arrows
30962 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
30964 deferListener(event, this, this.value);
30967 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
30968 if ($sniffer.hasEvent('paste')) {
30969 element.on('paste cut', deferListener);
30973 // if user paste into input using mouse on older browser
30974 // or form autocomplete on newer browser, we need "change" event to catch it
30975 element.on('change', listener);
30977 ctrl.$render = function() {
30978 // Workaround for Firefox validation #12102.
30979 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
30980 if (element.val() !== value) {
30981 element.val(value);
30986 function weekParser(isoWeek, existingDate) {
30987 if (isDate(isoWeek)) {
30991 if (isString(isoWeek)) {
30992 WEEK_REGEXP.lastIndex = 0;
30993 var parts = WEEK_REGEXP.exec(isoWeek);
30995 var year = +parts[1],
31001 firstThurs = getFirstThursdayOfYear(year),
31002 addDays = (week - 1) * 7;
31004 if (existingDate) {
31005 hours = existingDate.getHours();
31006 minutes = existingDate.getMinutes();
31007 seconds = existingDate.getSeconds();
31008 milliseconds = existingDate.getMilliseconds();
31011 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
31018 function createDateParser(regexp, mapping) {
31019 return function(iso, date) {
31026 if (isString(iso)) {
31027 // When a date is JSON'ified to wraps itself inside of an extra
31028 // set of double quotes. This makes the date parsing code unable
31029 // to match the date string and parse it as a date.
31030 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
31031 iso = iso.substring(1, iso.length - 1);
31033 if (ISO_DATE_REGEXP.test(iso)) {
31034 return new Date(iso);
31036 regexp.lastIndex = 0;
31037 parts = regexp.exec(iso);
31043 yyyy: date.getFullYear(),
31044 MM: date.getMonth() + 1,
31045 dd: date.getDate(),
31046 HH: date.getHours(),
31047 mm: date.getMinutes(),
31048 ss: date.getSeconds(),
31049 sss: date.getMilliseconds() / 1000
31052 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
31055 forEach(parts, function(part, index) {
31056 if (index < mapping.length) {
31057 map[mapping[index]] = +part;
31060 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
31068 function createDateInputType(type, regexp, parseDate, format) {
31069 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
31070 badInputChecker(scope, element, attr, ctrl);
31071 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
31072 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
31075 ctrl.$$parserName = type;
31076 ctrl.$parsers.push(function(value) {
31077 if (ctrl.$isEmpty(value)) return null;
31078 if (regexp.test(value)) {
31079 // Note: We cannot read ctrl.$modelValue, as there might be a different
31080 // parser/formatter in the processing chain so that the model
31081 // contains some different data format!
31082 var parsedDate = parseDate(value, previousDate);
31084 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
31091 ctrl.$formatters.push(function(value) {
31092 if (value && !isDate(value)) {
31093 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
31095 if (isValidDate(value)) {
31096 previousDate = value;
31097 if (previousDate && timezone) {
31098 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
31100 return $filter('date')(value, format, timezone);
31102 previousDate = null;
31107 if (isDefined(attr.min) || attr.ngMin) {
31109 ctrl.$validators.min = function(value) {
31110 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
31112 attr.$observe('min', function(val) {
31113 minVal = parseObservedDateValue(val);
31118 if (isDefined(attr.max) || attr.ngMax) {
31120 ctrl.$validators.max = function(value) {
31121 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
31123 attr.$observe('max', function(val) {
31124 maxVal = parseObservedDateValue(val);
31129 function isValidDate(value) {
31130 // Invalid Date: getTime() returns NaN
31131 return value && !(value.getTime && value.getTime() !== value.getTime());
31134 function parseObservedDateValue(val) {
31135 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
31140 function badInputChecker(scope, element, attr, ctrl) {
31141 var node = element[0];
31142 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
31143 if (nativeValidation) {
31144 ctrl.$parsers.push(function(value) {
31145 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
31146 // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
31147 // - also sets validity.badInput (should only be validity.typeMismatch).
31148 // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
31149 // - can ignore this case as we can still read out the erroneous email...
31150 return validity.badInput && !validity.typeMismatch ? undefined : value;
31155 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31156 badInputChecker(scope, element, attr, ctrl);
31157 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
31159 ctrl.$$parserName = 'number';
31160 ctrl.$parsers.push(function(value) {
31161 if (ctrl.$isEmpty(value)) return null;
31162 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
31166 ctrl.$formatters.push(function(value) {
31167 if (!ctrl.$isEmpty(value)) {
31168 if (!isNumber(value)) {
31169 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
31171 value = value.toString();
31176 if (isDefined(attr.min) || attr.ngMin) {
31178 ctrl.$validators.min = function(value) {
31179 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
31182 attr.$observe('min', function(val) {
31183 if (isDefined(val) && !isNumber(val)) {
31184 val = parseFloat(val, 10);
31186 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
31187 // TODO(matsko): implement validateLater to reduce number of validations
31192 if (isDefined(attr.max) || attr.ngMax) {
31194 ctrl.$validators.max = function(value) {
31195 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
31198 attr.$observe('max', function(val) {
31199 if (isDefined(val) && !isNumber(val)) {
31200 val = parseFloat(val, 10);
31202 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
31203 // TODO(matsko): implement validateLater to reduce number of validations
31209 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31210 // Note: no badInputChecker here by purpose as `url` is only a validation
31211 // in browsers, i.e. we can always read out input.value even if it is not valid!
31212 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
31213 stringBasedInputType(ctrl);
31215 ctrl.$$parserName = 'url';
31216 ctrl.$validators.url = function(modelValue, viewValue) {
31217 var value = modelValue || viewValue;
31218 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
31222 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31223 // Note: no badInputChecker here by purpose as `url` is only a validation
31224 // in browsers, i.e. we can always read out input.value even if it is not valid!
31225 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
31226 stringBasedInputType(ctrl);
31228 ctrl.$$parserName = 'email';
31229 ctrl.$validators.email = function(modelValue, viewValue) {
31230 var value = modelValue || viewValue;
31231 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
31235 function radioInputType(scope, element, attr, ctrl) {
31236 // make the name unique, if not defined
31237 if (isUndefined(attr.name)) {
31238 element.attr('name', nextUid());
31241 var listener = function(ev) {
31242 if (element[0].checked) {
31243 ctrl.$setViewValue(attr.value, ev && ev.type);
31247 element.on('click', listener);
31249 ctrl.$render = function() {
31250 var value = attr.value;
31251 element[0].checked = (value == ctrl.$viewValue);
31254 attr.$observe('value', ctrl.$render);
31257 function parseConstantExpr($parse, context, name, expression, fallback) {
31259 if (isDefined(expression)) {
31260 parseFn = $parse(expression);
31261 if (!parseFn.constant) {
31262 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
31263 '`{1}`.', name, expression);
31265 return parseFn(context);
31270 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
31271 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
31272 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
31274 var listener = function(ev) {
31275 ctrl.$setViewValue(element[0].checked, ev && ev.type);
31278 element.on('click', listener);
31280 ctrl.$render = function() {
31281 element[0].checked = ctrl.$viewValue;
31284 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
31285 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
31286 // it to a boolean.
31287 ctrl.$isEmpty = function(value) {
31288 return value === false;
31291 ctrl.$formatters.push(function(value) {
31292 return equals(value, trueValue);
31295 ctrl.$parsers.push(function(value) {
31296 return value ? trueValue : falseValue;
31307 * HTML textarea element control with angular data-binding. The data-binding and validation
31308 * properties of this element are exactly the same as those of the
31309 * {@link ng.directive:input input element}.
31311 * @param {string} ngModel Assignable angular expression to data-bind to.
31312 * @param {string=} name Property name of the form under which the control is published.
31313 * @param {string=} required Sets `required` validation error key if the value is not entered.
31314 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31315 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31316 * `required` when you want to data-bind to the `required` attribute.
31317 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31319 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31320 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
31322 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
31323 * a RegExp found by evaluating the Angular expression given in the attribute value.
31324 * If the expression evaluates to a RegExp object, then this is used directly.
31325 * If the expression evaluates to a string, then it will be converted to a RegExp
31326 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31327 * `new RegExp('^abc$')`.<br />
31328 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31329 * start at the index of the last search's match, thus not taking the whole input value into
31331 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31332 * interaction with the input element.
31333 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
31343 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
31344 * input state control, and validation.
31345 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
31347 * <div class="alert alert-warning">
31348 * **Note:** Not every feature offered is available for all input types.
31349 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
31352 * @param {string} ngModel Assignable angular expression to data-bind to.
31353 * @param {string=} name Property name of the form under which the control is published.
31354 * @param {string=} required Sets `required` validation error key if the value is not entered.
31355 * @param {boolean=} ngRequired Sets `required` attribute if set to true
31356 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31358 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31359 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
31361 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
31362 * a RegExp found by evaluating the Angular expression given in the attribute value.
31363 * If the expression evaluates to a RegExp object, then this is used directly.
31364 * If the expression evaluates to a string, then it will be converted to a RegExp
31365 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31366 * `new RegExp('^abc$')`.<br />
31367 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31368 * start at the index of the last search's match, thus not taking the whole input value into
31370 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31371 * interaction with the input element.
31372 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
31373 * This parameter is ignored for input[type=password] controls, which will never trim the
31377 <example name="input-directive" module="inputExample">
31378 <file name="index.html">
31380 angular.module('inputExample', [])
31381 .controller('ExampleController', ['$scope', function($scope) {
31382 $scope.user = {name: 'guest', last: 'visitor'};
31385 <div ng-controller="ExampleController">
31386 <form name="myForm">
31389 <input type="text" name="userName" ng-model="user.name" required>
31392 <span class="error" ng-show="myForm.userName.$error.required">
31397 <input type="text" name="lastName" ng-model="user.last"
31398 ng-minlength="3" ng-maxlength="10">
31401 <span class="error" ng-show="myForm.lastName.$error.minlength">
31403 <span class="error" ng-show="myForm.lastName.$error.maxlength">
31408 <tt>user = {{user}}</tt><br/>
31409 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
31410 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
31411 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
31412 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
31413 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31414 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31415 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
31416 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
31419 <file name="protractor.js" type="protractor">
31420 var user = element(by.exactBinding('user'));
31421 var userNameValid = element(by.binding('myForm.userName.$valid'));
31422 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
31423 var lastNameError = element(by.binding('myForm.lastName.$error'));
31424 var formValid = element(by.binding('myForm.$valid'));
31425 var userNameInput = element(by.model('user.name'));
31426 var userLastInput = element(by.model('user.last'));
31428 it('should initialize to model', function() {
31429 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
31430 expect(userNameValid.getText()).toContain('true');
31431 expect(formValid.getText()).toContain('true');
31434 it('should be invalid if empty when required', function() {
31435 userNameInput.clear();
31436 userNameInput.sendKeys('');
31438 expect(user.getText()).toContain('{"last":"visitor"}');
31439 expect(userNameValid.getText()).toContain('false');
31440 expect(formValid.getText()).toContain('false');
31443 it('should be valid if empty when min length is set', function() {
31444 userLastInput.clear();
31445 userLastInput.sendKeys('');
31447 expect(user.getText()).toContain('{"name":"guest","last":""}');
31448 expect(lastNameValid.getText()).toContain('true');
31449 expect(formValid.getText()).toContain('true');
31452 it('should be invalid if less than required min length', function() {
31453 userLastInput.clear();
31454 userLastInput.sendKeys('xx');
31456 expect(user.getText()).toContain('{"name":"guest"}');
31457 expect(lastNameValid.getText()).toContain('false');
31458 expect(lastNameError.getText()).toContain('minlength');
31459 expect(formValid.getText()).toContain('false');
31462 it('should be invalid if longer than max length', function() {
31463 userLastInput.clear();
31464 userLastInput.sendKeys('some ridiculously long name');
31466 expect(user.getText()).toContain('{"name":"guest"}');
31467 expect(lastNameValid.getText()).toContain('false');
31468 expect(lastNameError.getText()).toContain('maxlength');
31469 expect(formValid.getText()).toContain('false');
31474 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
31475 function($browser, $sniffer, $filter, $parse) {
31478 require: ['?ngModel'],
31480 pre: function(scope, element, attr, ctrls) {
31482 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
31483 $browser, $filter, $parse);
31492 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
31498 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
31499 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
31502 * `ngValue` is useful when dynamically generating lists of radio buttons using
31503 * {@link ngRepeat `ngRepeat`}, as shown below.
31505 * Likewise, `ngValue` can be used to generate `<option>` elements for
31506 * the {@link select `select`} element. In that case however, only strings are supported
31507 * for the `value `attribute, so the resulting `ngModel` will always be a string.
31508 * Support for `select` models with non-string values is available via `ngOptions`.
31511 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
31512 * of the `input` element
31515 <example name="ngValue-directive" module="valueExample">
31516 <file name="index.html">
31518 angular.module('valueExample', [])
31519 .controller('ExampleController', ['$scope', function($scope) {
31520 $scope.names = ['pizza', 'unicorns', 'robots'];
31521 $scope.my = { favorite: 'unicorns' };
31524 <form ng-controller="ExampleController">
31525 <h2>Which is your favorite?</h2>
31526 <label ng-repeat="name in names" for="{{name}}">
31528 <input type="radio"
31529 ng-model="my.favorite"
31534 <div>You chose {{my.favorite}}</div>
31537 <file name="protractor.js" type="protractor">
31538 var favorite = element(by.binding('my.favorite'));
31540 it('should initialize to model', function() {
31541 expect(favorite.getText()).toContain('unicorns');
31543 it('should bind the values to the inputs', function() {
31544 element.all(by.model('my.favorite')).get(0).click();
31545 expect(favorite.getText()).toContain('pizza');
31550 var ngValueDirective = function() {
31554 compile: function(tpl, tplAttr) {
31555 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
31556 return function ngValueConstantLink(scope, elm, attr) {
31557 attr.$set('value', scope.$eval(attr.ngValue));
31560 return function ngValueLink(scope, elm, attr) {
31561 scope.$watch(attr.ngValue, function valueWatchAction(value) {
31562 attr.$set('value', value);
31576 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
31577 * with the value of a given expression, and to update the text content when the value of that
31578 * expression changes.
31580 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
31581 * `{{ expression }}` which is similar but less verbose.
31583 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
31584 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
31585 * element attribute, it makes the bindings invisible to the user while the page is loading.
31587 * An alternative solution to this problem would be using the
31588 * {@link ng.directive:ngCloak ngCloak} directive.
31592 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
31595 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
31596 <example module="bindExample">
31597 <file name="index.html">
31599 angular.module('bindExample', [])
31600 .controller('ExampleController', ['$scope', function($scope) {
31601 $scope.name = 'Whirled';
31604 <div ng-controller="ExampleController">
31605 <label>Enter name: <input type="text" ng-model="name"></label><br>
31606 Hello <span ng-bind="name"></span>!
31609 <file name="protractor.js" type="protractor">
31610 it('should check ng-bind', function() {
31611 var nameInput = element(by.model('name'));
31613 expect(element(by.binding('name')).getText()).toBe('Whirled');
31615 nameInput.sendKeys('world');
31616 expect(element(by.binding('name')).getText()).toBe('world');
31621 var ngBindDirective = ['$compile', function($compile) {
31624 compile: function ngBindCompile(templateElement) {
31625 $compile.$$addBindingClass(templateElement);
31626 return function ngBindLink(scope, element, attr) {
31627 $compile.$$addBindingInfo(element, attr.ngBind);
31628 element = element[0];
31629 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
31630 element.textContent = isUndefined(value) ? '' : value;
31640 * @name ngBindTemplate
31643 * The `ngBindTemplate` directive specifies that the element
31644 * text content should be replaced with the interpolation of the template
31645 * in the `ngBindTemplate` attribute.
31646 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
31647 * expressions. This directive is needed since some HTML elements
31648 * (such as TITLE and OPTION) cannot contain SPAN elements.
31651 * @param {string} ngBindTemplate template of form
31652 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
31655 * Try it here: enter text in text box and watch the greeting change.
31656 <example module="bindExample">
31657 <file name="index.html">
31659 angular.module('bindExample', [])
31660 .controller('ExampleController', ['$scope', function($scope) {
31661 $scope.salutation = 'Hello';
31662 $scope.name = 'World';
31665 <div ng-controller="ExampleController">
31666 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
31667 <label>Name: <input type="text" ng-model="name"></label><br>
31668 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
31671 <file name="protractor.js" type="protractor">
31672 it('should check ng-bind', function() {
31673 var salutationElem = element(by.binding('salutation'));
31674 var salutationInput = element(by.model('salutation'));
31675 var nameInput = element(by.model('name'));
31677 expect(salutationElem.getText()).toBe('Hello World!');
31679 salutationInput.clear();
31680 salutationInput.sendKeys('Greetings');
31682 nameInput.sendKeys('user');
31684 expect(salutationElem.getText()).toBe('Greetings user!');
31689 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
31691 compile: function ngBindTemplateCompile(templateElement) {
31692 $compile.$$addBindingClass(templateElement);
31693 return function ngBindTemplateLink(scope, element, attr) {
31694 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
31695 $compile.$$addBindingInfo(element, interpolateFn.expressions);
31696 element = element[0];
31697 attr.$observe('ngBindTemplate', function(value) {
31698 element.textContent = isUndefined(value) ? '' : value;
31711 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
31712 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
31713 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
31714 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
31715 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
31717 * You may also bypass sanitization for values you know are safe. To do so, bind to
31718 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
31719 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
31721 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
31722 * will have an exception (instead of an exploit.)
31725 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
31729 <example module="bindHtmlExample" deps="angular-sanitize.js">
31730 <file name="index.html">
31731 <div ng-controller="ExampleController">
31732 <p ng-bind-html="myHTML"></p>
31736 <file name="script.js">
31737 angular.module('bindHtmlExample', ['ngSanitize'])
31738 .controller('ExampleController', ['$scope', function($scope) {
31740 'I am an <code>HTML</code>string with ' +
31741 '<a href="#">links!</a> and other <em>stuff</em>';
31745 <file name="protractor.js" type="protractor">
31746 it('should check ng-bind-html', function() {
31747 expect(element(by.binding('myHTML')).getText()).toBe(
31748 'I am an HTMLstring with links! and other stuff');
31753 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
31756 compile: function ngBindHtmlCompile(tElement, tAttrs) {
31757 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
31758 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
31759 return (value || '').toString();
31761 $compile.$$addBindingClass(tElement);
31763 return function ngBindHtmlLink(scope, element, attr) {
31764 $compile.$$addBindingInfo(element, attr.ngBindHtml);
31766 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
31767 // we re-evaluate the expr because we want a TrustedValueHolderType
31768 // for $sce, not a string
31769 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
31781 * Evaluate the given expression when the user changes the input.
31782 * The expression is evaluated immediately, unlike the JavaScript onchange event
31783 * which only triggers at the end of a change (usually, when the user leaves the
31784 * form element or presses the return key).
31786 * The `ngChange` expression is only evaluated when a change in the input value causes
31787 * a new value to be committed to the model.
31789 * It will not be evaluated:
31790 * * if the value returned from the `$parsers` transformation pipeline has not changed
31791 * * if the input has continued to be invalid since the model will stay `null`
31792 * * if the model is changed programmatically and not by a change to the input value
31795 * Note, this directive requires `ngModel` to be present.
31798 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
31802 * <example name="ngChange-directive" module="changeExample">
31803 * <file name="index.html">
31805 * angular.module('changeExample', [])
31806 * .controller('ExampleController', ['$scope', function($scope) {
31807 * $scope.counter = 0;
31808 * $scope.change = function() {
31809 * $scope.counter++;
31813 * <div ng-controller="ExampleController">
31814 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
31815 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
31816 * <label for="ng-change-example2">Confirmed</label><br />
31817 * <tt>debug = {{confirmed}}</tt><br/>
31818 * <tt>counter = {{counter}}</tt><br/>
31821 * <file name="protractor.js" type="protractor">
31822 * var counter = element(by.binding('counter'));
31823 * var debug = element(by.binding('confirmed'));
31825 * it('should evaluate the expression if changing from view', function() {
31826 * expect(counter.getText()).toContain('0');
31828 * element(by.id('ng-change-example1')).click();
31830 * expect(counter.getText()).toContain('1');
31831 * expect(debug.getText()).toContain('true');
31834 * it('should not evaluate the expression if changing from model', function() {
31835 * element(by.id('ng-change-example2')).click();
31837 * expect(counter.getText()).toContain('0');
31838 * expect(debug.getText()).toContain('true');
31843 var ngChangeDirective = valueFn({
31845 require: 'ngModel',
31846 link: function(scope, element, attr, ctrl) {
31847 ctrl.$viewChangeListeners.push(function() {
31848 scope.$eval(attr.ngChange);
31853 function classDirective(name, selector) {
31854 name = 'ngClass' + name;
31855 return ['$animate', function($animate) {
31858 link: function(scope, element, attr) {
31861 scope.$watch(attr[name], ngClassWatchAction, true);
31863 attr.$observe('class', function(value) {
31864 ngClassWatchAction(scope.$eval(attr[name]));
31868 if (name !== 'ngClass') {
31869 scope.$watch('$index', function($index, old$index) {
31870 // jshint bitwise: false
31871 var mod = $index & 1;
31872 if (mod !== (old$index & 1)) {
31873 var classes = arrayClasses(scope.$eval(attr[name]));
31875 addClasses(classes) :
31876 removeClasses(classes);
31881 function addClasses(classes) {
31882 var newClasses = digestClassCounts(classes, 1);
31883 attr.$addClass(newClasses);
31886 function removeClasses(classes) {
31887 var newClasses = digestClassCounts(classes, -1);
31888 attr.$removeClass(newClasses);
31891 function digestClassCounts(classes, count) {
31892 // Use createMap() to prevent class assumptions involving property
31893 // names in Object.prototype
31894 var classCounts = element.data('$classCounts') || createMap();
31895 var classesToUpdate = [];
31896 forEach(classes, function(className) {
31897 if (count > 0 || classCounts[className]) {
31898 classCounts[className] = (classCounts[className] || 0) + count;
31899 if (classCounts[className] === +(count > 0)) {
31900 classesToUpdate.push(className);
31904 element.data('$classCounts', classCounts);
31905 return classesToUpdate.join(' ');
31908 function updateClasses(oldClasses, newClasses) {
31909 var toAdd = arrayDifference(newClasses, oldClasses);
31910 var toRemove = arrayDifference(oldClasses, newClasses);
31911 toAdd = digestClassCounts(toAdd, 1);
31912 toRemove = digestClassCounts(toRemove, -1);
31913 if (toAdd && toAdd.length) {
31914 $animate.addClass(element, toAdd);
31916 if (toRemove && toRemove.length) {
31917 $animate.removeClass(element, toRemove);
31921 function ngClassWatchAction(newVal) {
31922 if (selector === true || scope.$index % 2 === selector) {
31923 var newClasses = arrayClasses(newVal || []);
31925 addClasses(newClasses);
31926 } else if (!equals(newVal,oldVal)) {
31927 var oldClasses = arrayClasses(oldVal);
31928 updateClasses(oldClasses, newClasses);
31931 oldVal = shallowCopy(newVal);
31936 function arrayDifference(tokens1, tokens2) {
31940 for (var i = 0; i < tokens1.length; i++) {
31941 var token = tokens1[i];
31942 for (var j = 0; j < tokens2.length; j++) {
31943 if (token == tokens2[j]) continue outer;
31945 values.push(token);
31950 function arrayClasses(classVal) {
31952 if (isArray(classVal)) {
31953 forEach(classVal, function(v) {
31954 classes = classes.concat(arrayClasses(v));
31957 } else if (isString(classVal)) {
31958 return classVal.split(' ');
31959 } else if (isObject(classVal)) {
31960 forEach(classVal, function(v, k) {
31962 classes = classes.concat(k.split(' '));
31978 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
31979 * an expression that represents all classes to be added.
31981 * The directive operates in three different ways, depending on which of three types the expression
31984 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
31987 * 2. If the expression evaluates to an object, then for each key-value pair of the
31988 * object with a truthy value the corresponding key is used as a class name.
31990 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
31991 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
31992 * to give you more control over what CSS classes appear. See the code below for an example of this.
31995 * The directive won't add duplicate classes if a particular class was already set.
31997 * When the expression changes, the previously added classes are removed and only then are the
31998 * new classes added.
32001 * **add** - happens just before the class is applied to the elements
32003 * **remove** - happens just before the class is removed from the element
32006 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
32007 * of the evaluation can be a string representing space delimited class
32008 * names, an array, or a map of class names to boolean values. In the case of a map, the
32009 * names of the properties whose values are truthy will be added as css classes to the
32012 * @example Example that demonstrates basic bindings via ngClass directive.
32014 <file name="index.html">
32015 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
32017 <input type="checkbox" ng-model="deleted">
32018 deleted (apply "strike" class)
32021 <input type="checkbox" ng-model="important">
32022 important (apply "bold" class)
32025 <input type="checkbox" ng-model="error">
32026 error (apply "has-error" class)
32029 <p ng-class="style">Using String Syntax</p>
32030 <input type="text" ng-model="style"
32031 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
32033 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
32034 <input ng-model="style1"
32035 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
32036 <input ng-model="style2"
32037 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
32038 <input ng-model="style3"
32039 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
32041 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
32042 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
32043 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
32045 <file name="style.css">
32047 text-decoration: line-through;
32057 background-color: yellow;
32063 <file name="protractor.js" type="protractor">
32064 var ps = element.all(by.css('p'));
32066 it('should let you toggle the class', function() {
32068 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
32069 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
32071 element(by.model('important')).click();
32072 expect(ps.first().getAttribute('class')).toMatch(/bold/);
32074 element(by.model('error')).click();
32075 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
32078 it('should let you toggle string example', function() {
32079 expect(ps.get(1).getAttribute('class')).toBe('');
32080 element(by.model('style')).clear();
32081 element(by.model('style')).sendKeys('red');
32082 expect(ps.get(1).getAttribute('class')).toBe('red');
32085 it('array example should have 3 classes', function() {
32086 expect(ps.get(2).getAttribute('class')).toBe('');
32087 element(by.model('style1')).sendKeys('bold');
32088 element(by.model('style2')).sendKeys('strike');
32089 element(by.model('style3')).sendKeys('red');
32090 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
32093 it('array with map example should have 2 classes', function() {
32094 expect(ps.last().getAttribute('class')).toBe('');
32095 element(by.model('style4')).sendKeys('bold');
32096 element(by.model('warning')).click();
32097 expect(ps.last().getAttribute('class')).toBe('bold orange');
32104 The example below demonstrates how to perform animations using ngClass.
32106 <example module="ngAnimate" deps="angular-animate.js" animations="true">
32107 <file name="index.html">
32108 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
32109 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
32111 <span class="base-class" ng-class="myVar">Sample Text</span>
32113 <file name="style.css">
32115 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
32118 .base-class.my-class {
32123 <file name="protractor.js" type="protractor">
32124 it('should check ng-class', function() {
32125 expect(element(by.css('.base-class')).getAttribute('class')).not.
32126 toMatch(/my-class/);
32128 element(by.id('setbtn')).click();
32130 expect(element(by.css('.base-class')).getAttribute('class')).
32131 toMatch(/my-class/);
32133 element(by.id('clearbtn')).click();
32135 expect(element(by.css('.base-class')).getAttribute('class')).not.
32136 toMatch(/my-class/);
32142 ## ngClass and pre-existing CSS3 Transitions/Animations
32143 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
32144 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
32145 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
32146 to view the step by step details of {@link $animate#addClass $animate.addClass} and
32147 {@link $animate#removeClass $animate.removeClass}.
32149 var ngClassDirective = classDirective('', true);
32157 * The `ngClassOdd` and `ngClassEven` directives work exactly as
32158 * {@link ng.directive:ngClass ngClass}, except they work in
32159 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
32161 * This directive can be applied only within the scope of an
32162 * {@link ng.directive:ngRepeat ngRepeat}.
32165 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
32166 * of the evaluation can be a string representing space delimited class names or an array.
32170 <file name="index.html">
32171 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
32172 <li ng-repeat="name in names">
32173 <span ng-class-odd="'odd'" ng-class-even="'even'">
32179 <file name="style.css">
32187 <file name="protractor.js" type="protractor">
32188 it('should check ng-class-odd and ng-class-even', function() {
32189 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
32191 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
32197 var ngClassOddDirective = classDirective('Odd', 0);
32201 * @name ngClassEven
32205 * The `ngClassOdd` and `ngClassEven` directives work exactly as
32206 * {@link ng.directive:ngClass ngClass}, except they work in
32207 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
32209 * This directive can be applied only within the scope of an
32210 * {@link ng.directive:ngRepeat ngRepeat}.
32213 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
32214 * result of the evaluation can be a string representing space delimited class names or an array.
32218 <file name="index.html">
32219 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
32220 <li ng-repeat="name in names">
32221 <span ng-class-odd="'odd'" ng-class-even="'even'">
32222 {{name}}
32227 <file name="style.css">
32235 <file name="protractor.js" type="protractor">
32236 it('should check ng-class-odd and ng-class-even', function() {
32237 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
32239 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
32245 var ngClassEvenDirective = classDirective('Even', 1);
32253 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
32254 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
32255 * directive to avoid the undesirable flicker effect caused by the html template display.
32257 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
32258 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
32259 * of the browser view.
32261 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
32262 * `angular.min.js`.
32263 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
32266 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
32267 * display: none !important;
32271 * When this css rule is loaded by the browser, all html elements (including their children) that
32272 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
32273 * during the compilation of the template it deletes the `ngCloak` element attribute, making
32274 * the compiled element visible.
32276 * For the best result, the `angular.js` script must be loaded in the head section of the html
32277 * document; alternatively, the css rule above must be included in the external stylesheet of the
32284 <file name="index.html">
32285 <div id="template1" ng-cloak>{{ 'hello' }}</div>
32286 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
32288 <file name="protractor.js" type="protractor">
32289 it('should remove the template directive and css class', function() {
32290 expect($('#template1').getAttribute('ng-cloak')).
32292 expect($('#template2').getAttribute('ng-cloak')).
32299 var ngCloakDirective = ngDirective({
32300 compile: function(element, attr) {
32301 attr.$set('ngCloak', undefined);
32302 element.removeClass('ng-cloak');
32308 * @name ngController
32311 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
32312 * supports the principles behind the Model-View-Controller design pattern.
32314 * MVC components in angular:
32316 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
32317 * are accessed through bindings.
32318 * * View — The template (HTML with data bindings) that is rendered into the View.
32319 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
32320 * logic behind the application to decorate the scope with functions and values
32322 * Note that you can also attach controllers to the DOM by declaring it in a route definition
32323 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
32324 * again using `ng-controller` in the template itself. This will cause the controller to be attached
32325 * and executed twice.
32330 * @param {expression} ngController Name of a constructor function registered with the current
32331 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
32332 * that on the current scope evaluates to a constructor function.
32334 * The controller instance can be published into a scope property by specifying
32335 * `ng-controller="as propertyName"`.
32337 * If the current `$controllerProvider` is configured to use globals (via
32338 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
32339 * also be the name of a globally accessible constructor function (not recommended).
32342 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
32343 * greeting are methods declared on the controller (see source tab). These methods can
32344 * easily be called from the angular markup. Any changes to the data are automatically reflected
32345 * in the View without the need for a manual update.
32347 * Two different declaration styles are included below:
32349 * * one binds methods and properties directly onto the controller using `this`:
32350 * `ng-controller="SettingsController1 as settings"`
32351 * * one injects `$scope` into the controller:
32352 * `ng-controller="SettingsController2"`
32354 * The second option is more common in the Angular community, and is generally used in boilerplates
32355 * and in this guide. However, there are advantages to binding properties directly to the controller
32356 * and avoiding scope.
32358 * * Using `controller as` makes it obvious which controller you are accessing in the template when
32359 * multiple controllers apply to an element.
32360 * * If you are writing your controllers as classes you have easier access to the properties and
32361 * methods, which will appear on the scope, from inside the controller code.
32362 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
32363 * inheritance masking primitives.
32365 * This example demonstrates the `controller as` syntax.
32367 * <example name="ngControllerAs" module="controllerAsExample">
32368 * <file name="index.html">
32369 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
32370 * <label>Name: <input type="text" ng-model="settings.name"/></label>
32371 * <button ng-click="settings.greet()">greet</button><br/>
32374 * <li ng-repeat="contact in settings.contacts">
32375 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
32376 * <option>phone</option>
32377 * <option>email</option>
32379 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
32380 * <button ng-click="settings.clearContact(contact)">clear</button>
32381 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
32383 * <li><button ng-click="settings.addContact()">add</button></li>
32387 * <file name="app.js">
32388 * angular.module('controllerAsExample', [])
32389 * .controller('SettingsController1', SettingsController1);
32391 * function SettingsController1() {
32392 * this.name = "John Smith";
32393 * this.contacts = [
32394 * {type: 'phone', value: '408 555 1212'},
32395 * {type: 'email', value: 'john.smith@example.org'} ];
32398 * SettingsController1.prototype.greet = function() {
32399 * alert(this.name);
32402 * SettingsController1.prototype.addContact = function() {
32403 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
32406 * SettingsController1.prototype.removeContact = function(contactToRemove) {
32407 * var index = this.contacts.indexOf(contactToRemove);
32408 * this.contacts.splice(index, 1);
32411 * SettingsController1.prototype.clearContact = function(contact) {
32412 * contact.type = 'phone';
32413 * contact.value = '';
32416 * <file name="protractor.js" type="protractor">
32417 * it('should check controller as', function() {
32418 * var container = element(by.id('ctrl-as-exmpl'));
32419 * expect(container.element(by.model('settings.name'))
32420 * .getAttribute('value')).toBe('John Smith');
32422 * var firstRepeat =
32423 * container.element(by.repeater('contact in settings.contacts').row(0));
32424 * var secondRepeat =
32425 * container.element(by.repeater('contact in settings.contacts').row(1));
32427 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32428 * .toBe('408 555 1212');
32430 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
32431 * .toBe('john.smith@example.org');
32433 * firstRepeat.element(by.buttonText('clear')).click();
32435 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32438 * container.element(by.buttonText('add')).click();
32440 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
32441 * .element(by.model('contact.value'))
32442 * .getAttribute('value'))
32443 * .toBe('yourname@example.org');
32448 * This example demonstrates the "attach to `$scope`" style of controller.
32450 * <example name="ngController" module="controllerExample">
32451 * <file name="index.html">
32452 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
32453 * <label>Name: <input type="text" ng-model="name"/></label>
32454 * <button ng-click="greet()">greet</button><br/>
32457 * <li ng-repeat="contact in contacts">
32458 * <select ng-model="contact.type" id="select_{{$index}}">
32459 * <option>phone</option>
32460 * <option>email</option>
32462 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
32463 * <button ng-click="clearContact(contact)">clear</button>
32464 * <button ng-click="removeContact(contact)">X</button>
32466 * <li>[ <button ng-click="addContact()">add</button> ]</li>
32470 * <file name="app.js">
32471 * angular.module('controllerExample', [])
32472 * .controller('SettingsController2', ['$scope', SettingsController2]);
32474 * function SettingsController2($scope) {
32475 * $scope.name = "John Smith";
32476 * $scope.contacts = [
32477 * {type:'phone', value:'408 555 1212'},
32478 * {type:'email', value:'john.smith@example.org'} ];
32480 * $scope.greet = function() {
32481 * alert($scope.name);
32484 * $scope.addContact = function() {
32485 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
32488 * $scope.removeContact = function(contactToRemove) {
32489 * var index = $scope.contacts.indexOf(contactToRemove);
32490 * $scope.contacts.splice(index, 1);
32493 * $scope.clearContact = function(contact) {
32494 * contact.type = 'phone';
32495 * contact.value = '';
32499 * <file name="protractor.js" type="protractor">
32500 * it('should check controller', function() {
32501 * var container = element(by.id('ctrl-exmpl'));
32503 * expect(container.element(by.model('name'))
32504 * .getAttribute('value')).toBe('John Smith');
32506 * var firstRepeat =
32507 * container.element(by.repeater('contact in contacts').row(0));
32508 * var secondRepeat =
32509 * container.element(by.repeater('contact in contacts').row(1));
32511 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32512 * .toBe('408 555 1212');
32513 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
32514 * .toBe('john.smith@example.org');
32516 * firstRepeat.element(by.buttonText('clear')).click();
32518 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32521 * container.element(by.buttonText('add')).click();
32523 * expect(container.element(by.repeater('contact in contacts').row(2))
32524 * .element(by.model('contact.value'))
32525 * .getAttribute('value'))
32526 * .toBe('yourname@example.org');
32532 var ngControllerDirective = [function() {
32548 * Angular has some features that can break certain
32549 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
32551 * If you intend to implement these rules then you must tell Angular not to use these features.
32553 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
32556 * The following rules affect Angular:
32558 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
32559 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
32560 * increase in the speed of evaluating Angular expressions.
32562 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
32563 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
32564 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
32565 * `angular-csp.css` in your HTML manually.
32567 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
32568 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
32569 * however, triggers a CSP error to be logged in the console:
32572 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
32573 * script in the following Content Security Policy directive: "default-src 'self'". Note that
32574 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
32577 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
32578 * directive on an element of the HTML document that appears before the `<script>` tag that loads
32579 * the `angular.js` file.
32581 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
32583 * You can specify which of the CSP related Angular features should be deactivated by providing
32584 * a value for the `ng-csp` attribute. The options are as follows:
32586 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
32588 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
32590 * You can use these values in the following combinations:
32593 * * No declaration means that Angular will assume that you can do inline styles, but it will do
32594 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
32597 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
32598 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
32601 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
32602 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
32604 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
32605 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
32607 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
32608 * styles nor use eval, which is the same as an empty: ng-csp.
32609 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
32612 * This example shows how to apply the `ngCsp` directive to the `html` tag.
32615 <html ng-app ng-csp>
32621 // Note: the suffix `.csp` in the example name triggers
32622 // csp mode in our http server!
32623 <example name="example.csp" module="cspExample" ng-csp="true">
32624 <file name="index.html">
32625 <div ng-controller="MainController as ctrl">
32627 <button ng-click="ctrl.inc()" id="inc">Increment</button>
32628 <span id="counter">
32634 <button ng-click="ctrl.evil()" id="evil">Evil</button>
32635 <span id="evilError">
32641 <file name="script.js">
32642 angular.module('cspExample', [])
32643 .controller('MainController', function() {
32645 this.inc = function() {
32648 this.evil = function() {
32649 // jshint evil:true
32653 this.evilError = e.message;
32658 <file name="protractor.js" type="protractor">
32659 var util, webdriver;
32661 var incBtn = element(by.id('inc'));
32662 var counter = element(by.id('counter'));
32663 var evilBtn = element(by.id('evil'));
32664 var evilError = element(by.id('evilError'));
32666 function getAndClearSevereErrors() {
32667 return browser.manage().logs().get('browser').then(function(browserLog) {
32668 return browserLog.filter(function(logEntry) {
32669 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
32674 function clearErrors() {
32675 getAndClearSevereErrors();
32678 function expectNoErrors() {
32679 getAndClearSevereErrors().then(function(filteredLog) {
32680 expect(filteredLog.length).toEqual(0);
32681 if (filteredLog.length) {
32682 console.log('browser console errors: ' + util.inspect(filteredLog));
32687 function expectError(regex) {
32688 getAndClearSevereErrors().then(function(filteredLog) {
32690 filteredLog.forEach(function(log) {
32691 if (log.message.match(regex)) {
32696 throw new Error('expected an error that matches ' + regex);
32701 beforeEach(function() {
32702 util = require('util');
32703 webdriver = require('protractor/node_modules/selenium-webdriver');
32706 // For now, we only test on Chrome,
32707 // as Safari does not load the page with Protractor's injected scripts,
32708 // and Firefox webdriver always disables content security policy (#6358)
32709 if (browser.params.browser !== 'chrome') {
32713 it('should not report errors when the page is loaded', function() {
32714 // clear errors so we are not dependent on previous tests
32716 // Need to reload the page as the page is already loaded when
32718 browser.driver.getCurrentUrl().then(function(url) {
32724 it('should evaluate expressions', function() {
32725 expect(counter.getText()).toEqual('0');
32727 expect(counter.getText()).toEqual('1');
32731 it('should throw and report an error when using "eval"', function() {
32733 expect(evilError.getText()).toMatch(/Content Security Policy/);
32734 expectError(/Content Security Policy/);
32740 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
32741 // bootstrap the system (before $parse is instantiated), for this reason we just have
32742 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
32749 * The ngClick directive allows you to specify custom behavior when
32750 * an element is clicked.
32754 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
32755 * click. ({@link guide/expression#-event- Event object is available as `$event`})
32759 <file name="index.html">
32760 <button ng-click="count = count + 1" ng-init="count=0">
32767 <file name="protractor.js" type="protractor">
32768 it('should check ng-click', function() {
32769 expect(element(by.binding('count')).getText()).toMatch('0');
32770 element(by.css('button')).click();
32771 expect(element(by.binding('count')).getText()).toMatch('1');
32777 * A collection of directives that allows creation of custom event handlers that are defined as
32778 * angular expressions and are compiled and executed within the current scope.
32780 var ngEventDirectives = {};
32782 // For events that might fire synchronously during DOM manipulation
32783 // we need to execute their event handlers asynchronously using $evalAsync,
32784 // so that they are not executed in an inconsistent state.
32785 var forceAsyncEvents = {
32790 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
32791 function(eventName) {
32792 var directiveName = directiveNormalize('ng-' + eventName);
32793 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
32796 compile: function($element, attr) {
32797 // We expose the powerful $event object on the scope that provides access to the Window,
32798 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
32799 // checks at the cost of speed since event handler expressions are not executed as
32800 // frequently as regular change detection.
32801 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
32802 return function ngEventHandler(scope, element) {
32803 element.on(eventName, function(event) {
32804 var callback = function() {
32805 fn(scope, {$event:event});
32807 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
32808 scope.$evalAsync(callback);
32810 scope.$apply(callback);
32825 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
32829 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
32830 * a dblclick. (The Event object is available as `$event`)
32834 <file name="index.html">
32835 <button ng-dblclick="count = count + 1" ng-init="count=0">
32836 Increment (on double click)
32846 * @name ngMousedown
32849 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
32853 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
32854 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
32858 <file name="index.html">
32859 <button ng-mousedown="count = count + 1" ng-init="count=0">
32860 Increment (on mouse down)
32873 * Specify custom behavior on mouseup event.
32877 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
32878 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
32882 <file name="index.html">
32883 <button ng-mouseup="count = count + 1" ng-init="count=0">
32884 Increment (on mouse up)
32893 * @name ngMouseover
32896 * Specify custom behavior on mouseover event.
32900 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
32901 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
32905 <file name="index.html">
32906 <button ng-mouseover="count = count + 1" ng-init="count=0">
32907 Increment (when mouse is over)
32917 * @name ngMouseenter
32920 * Specify custom behavior on mouseenter event.
32924 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
32925 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
32929 <file name="index.html">
32930 <button ng-mouseenter="count = count + 1" ng-init="count=0">
32931 Increment (when mouse enters)
32941 * @name ngMouseleave
32944 * Specify custom behavior on mouseleave event.
32948 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
32949 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
32953 <file name="index.html">
32954 <button ng-mouseleave="count = count + 1" ng-init="count=0">
32955 Increment (when mouse leaves)
32965 * @name ngMousemove
32968 * Specify custom behavior on mousemove event.
32972 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
32973 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
32977 <file name="index.html">
32978 <button ng-mousemove="count = count + 1" ng-init="count=0">
32979 Increment (when mouse moves)
32992 * Specify custom behavior on keydown event.
32996 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
32997 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
33001 <file name="index.html">
33002 <input ng-keydown="count = count + 1" ng-init="count=0">
33003 key down count: {{count}}
33014 * Specify custom behavior on keyup event.
33018 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
33019 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
33023 <file name="index.html">
33024 <p>Typing in the input box below updates the key count</p>
33025 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
33027 <p>Typing in the input box below updates the keycode</p>
33028 <input ng-keyup="event=$event">
33029 <p>event keyCode: {{ event.keyCode }}</p>
33030 <p>event altKey: {{ event.altKey }}</p>
33041 * Specify custom behavior on keypress event.
33044 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
33045 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
33046 * and can be interrogated for keyCode, altKey, etc.)
33050 <file name="index.html">
33051 <input ng-keypress="count = count + 1" ng-init="count=0">
33052 key press count: {{count}}
33063 * Enables binding angular expressions to onsubmit events.
33065 * Additionally it prevents the default action (which for form means sending the request to the
33066 * server and reloading the current page), but only if the form does not contain `action`,
33067 * `data-action`, or `x-action` attributes.
33069 * <div class="alert alert-warning">
33070 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
33071 * `ngSubmit` handlers together. See the
33072 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
33073 * for a detailed discussion of when `ngSubmit` may be triggered.
33078 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
33079 * ({@link guide/expression#-event- Event object is available as `$event`})
33082 <example module="submitExample">
33083 <file name="index.html">
33085 angular.module('submitExample', [])
33086 .controller('ExampleController', ['$scope', function($scope) {
33088 $scope.text = 'hello';
33089 $scope.submit = function() {
33091 $scope.list.push(this.text);
33097 <form ng-submit="submit()" ng-controller="ExampleController">
33098 Enter text and hit enter:
33099 <input type="text" ng-model="text" name="text" />
33100 <input type="submit" id="submit" value="Submit" />
33101 <pre>list={{list}}</pre>
33104 <file name="protractor.js" type="protractor">
33105 it('should check ng-submit', function() {
33106 expect(element(by.binding('list')).getText()).toBe('list=[]');
33107 element(by.css('#submit')).click();
33108 expect(element(by.binding('list')).getText()).toContain('hello');
33109 expect(element(by.model('text')).getAttribute('value')).toBe('');
33111 it('should ignore empty strings', function() {
33112 expect(element(by.binding('list')).getText()).toBe('list=[]');
33113 element(by.css('#submit')).click();
33114 element(by.css('#submit')).click();
33115 expect(element(by.binding('list')).getText()).toContain('hello');
33126 * Specify custom behavior on focus event.
33128 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
33129 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
33130 * during an `$apply` to ensure a consistent state.
33132 * @element window, input, select, textarea, a
33134 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
33135 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
33138 * See {@link ng.directive:ngClick ngClick}
33146 * Specify custom behavior on blur event.
33148 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
33149 * an element has lost focus.
33151 * Note: As the `blur` event is executed synchronously also during DOM manipulations
33152 * (e.g. removing a focussed input),
33153 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
33154 * during an `$apply` to ensure a consistent state.
33156 * @element window, input, select, textarea, a
33158 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
33159 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
33162 * See {@link ng.directive:ngClick ngClick}
33170 * Specify custom behavior on copy event.
33172 * @element window, input, select, textarea, a
33174 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
33175 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
33179 <file name="index.html">
33180 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
33191 * Specify custom behavior on cut event.
33193 * @element window, input, select, textarea, a
33195 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
33196 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
33200 <file name="index.html">
33201 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
33212 * Specify custom behavior on paste event.
33214 * @element window, input, select, textarea, a
33216 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
33217 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
33221 <file name="index.html">
33222 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
33235 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
33236 * {expression}. If the expression assigned to `ngIf` evaluates to a false
33237 * value then the element is removed from the DOM, otherwise a clone of the
33238 * element is reinserted into the DOM.
33240 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
33241 * element in the DOM rather than changing its visibility via the `display` css property. A common
33242 * case when this difference is significant is when using css selectors that rely on an element's
33243 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
33245 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
33246 * is created when the element is restored. The scope created within `ngIf` inherits from
33247 * its parent scope using
33248 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
33249 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
33250 * a javascript primitive defined in the parent scope. In this case any modifications made to the
33251 * variable within the child scope will override (hide) the value in the parent scope.
33253 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
33254 * is if an element's class attribute is directly modified after it's compiled, using something like
33255 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
33256 * the added class will be lost because the original compiled state is used to regenerate the element.
33258 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
33259 * and `leave` effects.
33262 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
33263 * leave - happens just before the `ngIf` contents are removed from the DOM
33268 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
33269 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
33270 * element is added to the DOM tree.
33273 <example module="ngAnimate" deps="angular-animate.js" animations="true">
33274 <file name="index.html">
33275 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
33277 <span ng-if="checked" class="animate-if">
33278 This is removed when the checkbox is unchecked.
33281 <file name="animations.css">
33284 border:1px solid black;
33288 .animate-if.ng-enter, .animate-if.ng-leave {
33289 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
33292 .animate-if.ng-enter,
33293 .animate-if.ng-leave.ng-leave-active {
33297 .animate-if.ng-leave,
33298 .animate-if.ng-enter.ng-enter-active {
33304 var ngIfDirective = ['$animate', function($animate) {
33306 multiElement: true,
33307 transclude: 'element',
33312 link: function($scope, $element, $attr, ctrl, $transclude) {
33313 var block, childScope, previousElements;
33314 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
33318 $transclude(function(clone, newScope) {
33319 childScope = newScope;
33320 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
33321 // Note: We only need the first/last node of the cloned nodes.
33322 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
33323 // by a directive with templateUrl when its template arrives.
33327 $animate.enter(clone, $element.parent(), $element);
33331 if (previousElements) {
33332 previousElements.remove();
33333 previousElements = null;
33336 childScope.$destroy();
33340 previousElements = getBlockNodes(block.clone);
33341 $animate.leave(previousElements).then(function() {
33342 previousElements = null;
33358 * Fetches, compiles and includes an external HTML fragment.
33360 * By default, the template URL is restricted to the same domain and protocol as the
33361 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
33362 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
33363 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
33364 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
33365 * ng.$sce Strict Contextual Escaping}.
33367 * In addition, the browser's
33368 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
33369 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
33370 * policy may further restrict whether the template is successfully loaded.
33371 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
33372 * access on some browsers.
33375 * enter - animation is used to bring new content into the browser.
33376 * leave - animation is used to animate existing content away.
33378 * The enter and leave animation occur concurrently.
33383 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
33384 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
33385 * @param {string=} onload Expression to evaluate when a new partial is loaded.
33386 * <div class="alert alert-warning">
33387 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
33388 * a function with the name on the window element, which will usually throw a
33389 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
33390 * different form that {@link guide/directive#normalization matches} `onload`.
33393 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
33394 * $anchorScroll} to scroll the viewport after the content is loaded.
33396 * - If the attribute is not set, disable scrolling.
33397 * - If the attribute is set without value, enable scrolling.
33398 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
33401 <example module="includeExample" deps="angular-animate.js" animations="true">
33402 <file name="index.html">
33403 <div ng-controller="ExampleController">
33404 <select ng-model="template" ng-options="t.name for t in templates">
33405 <option value="">(blank)</option>
33407 url of the template: <code>{{template.url}}</code>
33409 <div class="slide-animate-container">
33410 <div class="slide-animate" ng-include="template.url"></div>
33414 <file name="script.js">
33415 angular.module('includeExample', ['ngAnimate'])
33416 .controller('ExampleController', ['$scope', function($scope) {
33418 [ { name: 'template1.html', url: 'template1.html'},
33419 { name: 'template2.html', url: 'template2.html'} ];
33420 $scope.template = $scope.templates[0];
33423 <file name="template1.html">
33424 Content of template1.html
33426 <file name="template2.html">
33427 Content of template2.html
33429 <file name="animations.css">
33430 .slide-animate-container {
33433 border:1px solid black;
33442 .slide-animate.ng-enter, .slide-animate.ng-leave {
33443 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
33454 .slide-animate.ng-enter {
33457 .slide-animate.ng-enter.ng-enter-active {
33461 .slide-animate.ng-leave {
33464 .slide-animate.ng-leave.ng-leave-active {
33468 <file name="protractor.js" type="protractor">
33469 var templateSelect = element(by.model('template'));
33470 var includeElem = element(by.css('[ng-include]'));
33472 it('should load template1.html', function() {
33473 expect(includeElem.getText()).toMatch(/Content of template1.html/);
33476 it('should load template2.html', function() {
33477 if (browser.params.browser == 'firefox') {
33478 // Firefox can't handle using selects
33479 // See https://github.com/angular/protractor/issues/480
33482 templateSelect.click();
33483 templateSelect.all(by.css('option')).get(2).click();
33484 expect(includeElem.getText()).toMatch(/Content of template2.html/);
33487 it('should change to blank', function() {
33488 if (browser.params.browser == 'firefox') {
33489 // Firefox can't handle using selects
33492 templateSelect.click();
33493 templateSelect.all(by.css('option')).get(0).click();
33494 expect(includeElem.isPresent()).toBe(false);
33503 * @name ngInclude#$includeContentRequested
33504 * @eventType emit on the scope ngInclude was declared in
33506 * Emitted every time the ngInclude content is requested.
33508 * @param {Object} angularEvent Synthetic event object.
33509 * @param {String} src URL of content to load.
33515 * @name ngInclude#$includeContentLoaded
33516 * @eventType emit on the current ngInclude scope
33518 * Emitted every time the ngInclude content is reloaded.
33520 * @param {Object} angularEvent Synthetic event object.
33521 * @param {String} src URL of content to load.
33527 * @name ngInclude#$includeContentError
33528 * @eventType emit on the scope ngInclude was declared in
33530 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
33532 * @param {Object} angularEvent Synthetic event object.
33533 * @param {String} src URL of content to load.
33535 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
33536 function($templateRequest, $anchorScroll, $animate) {
33541 transclude: 'element',
33542 controller: angular.noop,
33543 compile: function(element, attr) {
33544 var srcExp = attr.ngInclude || attr.src,
33545 onloadExp = attr.onload || '',
33546 autoScrollExp = attr.autoscroll;
33548 return function(scope, $element, $attr, ctrl, $transclude) {
33549 var changeCounter = 0,
33554 var cleanupLastIncludeContent = function() {
33555 if (previousElement) {
33556 previousElement.remove();
33557 previousElement = null;
33559 if (currentScope) {
33560 currentScope.$destroy();
33561 currentScope = null;
33563 if (currentElement) {
33564 $animate.leave(currentElement).then(function() {
33565 previousElement = null;
33567 previousElement = currentElement;
33568 currentElement = null;
33572 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
33573 var afterAnimation = function() {
33574 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
33578 var thisChangeId = ++changeCounter;
33581 //set the 2nd param to true to ignore the template request error so that the inner
33582 //contents and scope can be cleaned up.
33583 $templateRequest(src, true).then(function(response) {
33584 if (thisChangeId !== changeCounter) return;
33585 var newScope = scope.$new();
33586 ctrl.template = response;
33588 // Note: This will also link all children of ng-include that were contained in the original
33589 // html. If that content contains controllers, ... they could pollute/change the scope.
33590 // However, using ng-include on an element with additional content does not make sense...
33591 // Note: We can't remove them in the cloneAttchFn of $transclude as that
33592 // function is called before linking the content, which would apply child
33593 // directives to non existing elements.
33594 var clone = $transclude(newScope, function(clone) {
33595 cleanupLastIncludeContent();
33596 $animate.enter(clone, null, $element).then(afterAnimation);
33599 currentScope = newScope;
33600 currentElement = clone;
33602 currentScope.$emit('$includeContentLoaded', src);
33603 scope.$eval(onloadExp);
33605 if (thisChangeId === changeCounter) {
33606 cleanupLastIncludeContent();
33607 scope.$emit('$includeContentError', src);
33610 scope.$emit('$includeContentRequested', src);
33612 cleanupLastIncludeContent();
33613 ctrl.template = null;
33621 // This directive is called during the $transclude call of the first `ngInclude` directive.
33622 // It will replace and compile the content of the element with the loaded template.
33623 // We need this directive so that the element content is already filled when
33624 // the link function of another directive on the same element as ngInclude
33626 var ngIncludeFillContentDirective = ['$compile',
33627 function($compile) {
33631 require: 'ngInclude',
33632 link: function(scope, $element, $attr, ctrl) {
33633 if (/SVG/.test($element[0].toString())) {
33634 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
33635 // support innerHTML, so detect this here and try to generate the contents
33638 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
33639 function namespaceAdaptedClone(clone) {
33640 $element.append(clone);
33641 }, {futureParentElement: $element});
33645 $element.html(ctrl.template);
33646 $compile($element.contents())(scope);
33657 * The `ngInit` directive allows you to evaluate an expression in the
33660 * <div class="alert alert-danger">
33661 * This directive can be abused to add unnecessary amounts of logic into your templates.
33662 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
33663 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
33664 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
33665 * rather than `ngInit` to initialize values on a scope.
33668 * <div class="alert alert-warning">
33669 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
33670 * sure you have parentheses to ensure correct operator precedence:
33671 * <pre class="prettyprint">
33672 * `<div ng-init="test1 = ($index | toString)"></div>`
33679 * @param {expression} ngInit {@link guide/expression Expression} to eval.
33682 <example module="initExample">
33683 <file name="index.html">
33685 angular.module('initExample', [])
33686 .controller('ExampleController', ['$scope', function($scope) {
33687 $scope.list = [['a', 'b'], ['c', 'd']];
33690 <div ng-controller="ExampleController">
33691 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
33692 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
33693 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
33698 <file name="protractor.js" type="protractor">
33699 it('should alias index positions', function() {
33700 var elements = element.all(by.css('.example-init'));
33701 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
33702 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
33703 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
33704 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
33709 var ngInitDirective = ngDirective({
33711 compile: function() {
33713 pre: function(scope, element, attrs) {
33714 scope.$eval(attrs.ngInit);
33725 * Text input that converts between a delimited string and an array of strings. The default
33726 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
33727 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
33729 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
33730 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
33731 * list item is respected. This implies that the user of the directive is responsible for
33732 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
33733 * tab or newline character.
33734 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
33735 * when joining the list items back together) and whitespace around each list item is stripped
33736 * before it is added to the model.
33738 * ### Example with Validation
33740 * <example name="ngList-directive" module="listExample">
33741 * <file name="app.js">
33742 * angular.module('listExample', [])
33743 * .controller('ExampleController', ['$scope', function($scope) {
33744 * $scope.names = ['morpheus', 'neo', 'trinity'];
33747 * <file name="index.html">
33748 * <form name="myForm" ng-controller="ExampleController">
33749 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
33750 * <span role="alert">
33751 * <span class="error" ng-show="myForm.namesInput.$error.required">
33755 * <tt>names = {{names}}</tt><br/>
33756 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
33757 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
33758 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
33759 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
33762 * <file name="protractor.js" type="protractor">
33763 * var listInput = element(by.model('names'));
33764 * var names = element(by.exactBinding('names'));
33765 * var valid = element(by.binding('myForm.namesInput.$valid'));
33766 * var error = element(by.css('span.error'));
33768 * it('should initialize to model', function() {
33769 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
33770 * expect(valid.getText()).toContain('true');
33771 * expect(error.getCssValue('display')).toBe('none');
33774 * it('should be invalid if empty', function() {
33775 * listInput.clear();
33776 * listInput.sendKeys('');
33778 * expect(names.getText()).toContain('');
33779 * expect(valid.getText()).toContain('false');
33780 * expect(error.getCssValue('display')).not.toBe('none');
33785 * ### Example - splitting on newline
33786 * <example name="ngList-directive-newlines">
33787 * <file name="index.html">
33788 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
33789 * <pre>{{ list | json }}</pre>
33791 * <file name="protractor.js" type="protractor">
33792 * it("should split the text by newlines", function() {
33793 * var listInput = element(by.model('list'));
33794 * var output = element(by.binding('list | json'));
33795 * listInput.sendKeys('abc\ndef\nghi');
33796 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
33802 * @param {string=} ngList optional delimiter that should be used to split the value.
33804 var ngListDirective = function() {
33808 require: 'ngModel',
33809 link: function(scope, element, attr, ctrl) {
33810 // We want to control whitespace trimming so we use this convoluted approach
33811 // to access the ngList attribute, which doesn't pre-trim the attribute
33812 var ngList = element.attr(attr.$attr.ngList) || ', ';
33813 var trimValues = attr.ngTrim !== 'false';
33814 var separator = trimValues ? trim(ngList) : ngList;
33816 var parse = function(viewValue) {
33817 // If the viewValue is invalid (say required but empty) it will be `undefined`
33818 if (isUndefined(viewValue)) return;
33823 forEach(viewValue.split(separator), function(value) {
33824 if (value) list.push(trimValues ? trim(value) : value);
33831 ctrl.$parsers.push(parse);
33832 ctrl.$formatters.push(function(value) {
33833 if (isArray(value)) {
33834 return value.join(ngList);
33840 // Override the standard $isEmpty because an empty array means the input is empty.
33841 ctrl.$isEmpty = function(value) {
33842 return !value || !value.length;
33848 /* global VALID_CLASS: true,
33849 INVALID_CLASS: true,
33850 PRISTINE_CLASS: true,
33852 UNTOUCHED_CLASS: true,
33853 TOUCHED_CLASS: true,
33856 var VALID_CLASS = 'ng-valid',
33857 INVALID_CLASS = 'ng-invalid',
33858 PRISTINE_CLASS = 'ng-pristine',
33859 DIRTY_CLASS = 'ng-dirty',
33860 UNTOUCHED_CLASS = 'ng-untouched',
33861 TOUCHED_CLASS = 'ng-touched',
33862 PENDING_CLASS = 'ng-pending';
33864 var ngModelMinErr = minErr('ngModel');
33868 * @name ngModel.NgModelController
33870 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
33871 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
33873 * @property {*} $modelValue The value in the model that the control is bound to.
33874 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
33875 the control reads value from the DOM. The functions are called in array order, each passing
33876 its return value through to the next. The last return value is forwarded to the
33877 {@link ngModel.NgModelController#$validators `$validators`} collection.
33879 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
33882 Returning `undefined` from a parser means a parse error occurred. In that case,
33883 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
33884 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
33885 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
33888 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
33889 the model value changes. The functions are called in reverse array order, each passing the value through to the
33890 next. The last return value is used as the actual DOM value.
33891 Used to format / convert values for display in the control.
33893 * function formatter(value) {
33895 * return value.toUpperCase();
33898 * ngModel.$formatters.push(formatter);
33901 * @property {Object.<string, function>} $validators A collection of validators that are applied
33902 * whenever the model value changes. The key value within the object refers to the name of the
33903 * validator while the function refers to the validation operation. The validation operation is
33904 * provided with the model value as an argument and must return a true or false value depending
33905 * on the response of that validation.
33908 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
33909 * var value = modelValue || viewValue;
33910 * return /[0-9]+/.test(value) &&
33911 * /[a-z]+/.test(value) &&
33912 * /[A-Z]+/.test(value) &&
33913 * /\W+/.test(value);
33917 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
33918 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
33919 * is expected to return a promise when it is run during the model validation process. Once the promise
33920 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
33921 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
33922 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
33923 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
33924 * will only run once all synchronous validators have passed.
33926 * Please note that if $http is used then it is important that the server returns a success HTTP response code
33927 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
33930 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
33931 * var value = modelValue || viewValue;
33933 * // Lookup user by username
33934 * return $http.get('/api/users/' + value).
33935 * then(function resolved() {
33936 * //username exists, this means validation fails
33937 * return $q.reject('exists');
33938 * }, function rejected() {
33939 * //username does not exist, therefore this validation passes
33945 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
33946 * view value has changed. It is called with no arguments, and its return value is ignored.
33947 * This can be used in place of additional $watches against the model value.
33949 * @property {Object} $error An object hash with all failing validator ids as keys.
33950 * @property {Object} $pending An object hash with all pending validator ids as keys.
33952 * @property {boolean} $untouched True if control has not lost focus yet.
33953 * @property {boolean} $touched True if control has lost focus.
33954 * @property {boolean} $pristine True if user has not interacted with the control yet.
33955 * @property {boolean} $dirty True if user has already interacted with the control.
33956 * @property {boolean} $valid True if there is no error.
33957 * @property {boolean} $invalid True if at least one error on the control.
33958 * @property {string} $name The name attribute of the control.
33962 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
33963 * The controller contains services for data-binding, validation, CSS updates, and value formatting
33964 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
33965 * listening to DOM events.
33966 * Such DOM related logic should be provided by other directives which make use of
33967 * `NgModelController` for data-binding to control elements.
33968 * Angular provides this DOM logic for most {@link input `input`} elements.
33969 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
33970 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
33973 * ### Custom Control Example
33974 * This example shows how to use `NgModelController` with a custom control to achieve
33975 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
33976 * collaborate together to achieve the desired result.
33978 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
33979 * contents be edited in place by the user.
33981 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
33982 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
33983 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
33984 * that content using the `$sce` service.
33986 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
33987 <file name="style.css">
33988 [contenteditable] {
33989 border: 1px solid black;
33990 background-color: white;
33995 border: 1px solid red;
33999 <file name="script.js">
34000 angular.module('customControl', ['ngSanitize']).
34001 directive('contenteditable', ['$sce', function($sce) {
34003 restrict: 'A', // only activate on element attribute
34004 require: '?ngModel', // get a hold of NgModelController
34005 link: function(scope, element, attrs, ngModel) {
34006 if (!ngModel) return; // do nothing if no ng-model
34008 // Specify how UI should be updated
34009 ngModel.$render = function() {
34010 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
34013 // Listen for change events to enable binding
34014 element.on('blur keyup change', function() {
34015 scope.$evalAsync(read);
34017 read(); // initialize
34019 // Write data to the model
34021 var html = element.html();
34022 // When we clear the content editable the browser leaves a <br> behind
34023 // If strip-br attribute is provided then we strip this out
34024 if ( attrs.stripBr && html == '<br>' ) {
34027 ngModel.$setViewValue(html);
34033 <file name="index.html">
34034 <form name="myForm">
34035 <div contenteditable
34036 name="myWidget" ng-model="userContent"
34038 required>Change me!</div>
34039 <span ng-show="myForm.myWidget.$error.required">Required!</span>
34041 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
34044 <file name="protractor.js" type="protractor">
34045 it('should data-bind and become invalid', function() {
34046 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
34047 // SafariDriver can't handle contenteditable
34048 // and Firefox driver can't clear contenteditables very well
34051 var contentEditable = element(by.css('[contenteditable]'));
34052 var content = 'Change me!';
34054 expect(contentEditable.getText()).toEqual(content);
34056 contentEditable.clear();
34057 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
34058 expect(contentEditable.getText()).toEqual('');
34059 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
34066 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
34067 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
34068 this.$viewValue = Number.NaN;
34069 this.$modelValue = Number.NaN;
34070 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
34071 this.$validators = {};
34072 this.$asyncValidators = {};
34073 this.$parsers = [];
34074 this.$formatters = [];
34075 this.$viewChangeListeners = [];
34076 this.$untouched = true;
34077 this.$touched = false;
34078 this.$pristine = true;
34079 this.$dirty = false;
34080 this.$valid = true;
34081 this.$invalid = false;
34082 this.$error = {}; // keep invalid keys here
34083 this.$$success = {}; // keep valid keys here
34084 this.$pending = undefined; // keep pending keys here
34085 this.$name = $interpolate($attr.name || '', false)($scope);
34086 this.$$parentForm = nullFormCtrl;
34088 var parsedNgModel = $parse($attr.ngModel),
34089 parsedNgModelAssign = parsedNgModel.assign,
34090 ngModelGet = parsedNgModel,
34091 ngModelSet = parsedNgModelAssign,
34092 pendingDebounce = null,
34096 this.$$setOptions = function(options) {
34097 ctrl.$options = options;
34098 if (options && options.getterSetter) {
34099 var invokeModelGetter = $parse($attr.ngModel + '()'),
34100 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
34102 ngModelGet = function($scope) {
34103 var modelValue = parsedNgModel($scope);
34104 if (isFunction(modelValue)) {
34105 modelValue = invokeModelGetter($scope);
34109 ngModelSet = function($scope, newValue) {
34110 if (isFunction(parsedNgModel($scope))) {
34111 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
34113 parsedNgModelAssign($scope, ctrl.$modelValue);
34116 } else if (!parsedNgModel.assign) {
34117 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
34118 $attr.ngModel, startingTag($element));
34124 * @name ngModel.NgModelController#$render
34127 * Called when the view needs to be updated. It is expected that the user of the ng-model
34128 * directive will implement this method.
34130 * The `$render()` method is invoked in the following situations:
34132 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
34133 * committed value then `$render()` is called to update the input control.
34134 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
34135 * the `$viewValue` are different from last time.
34137 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
34138 * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
34139 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
34140 * invoked if you only change a property on the objects.
34142 this.$render = noop;
34146 * @name ngModel.NgModelController#$isEmpty
34149 * This is called when we need to determine if the value of an input is empty.
34151 * For instance, the required directive does this to work out if the input has data or not.
34153 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
34155 * You can override this for input directives whose concept of being empty is different from the
34156 * default. The `checkboxInputType` directive does this because in its case a value of `false`
34159 * @param {*} value The value of the input to check for emptiness.
34160 * @returns {boolean} True if `value` is "empty".
34162 this.$isEmpty = function(value) {
34163 return isUndefined(value) || value === '' || value === null || value !== value;
34166 var currentValidationRunId = 0;
34170 * @name ngModel.NgModelController#$setValidity
34173 * Change the validity state, and notify the form.
34175 * This method can be called within $parsers/$formatters or a custom validation implementation.
34176 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
34177 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
34179 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
34180 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
34181 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
34182 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
34183 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
34184 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
34185 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
34186 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
34187 * Skipped is used by Angular when validators do not run because of parse errors and
34188 * when `$asyncValidators` do not run because any of the `$validators` failed.
34190 addSetValidityMethod({
34192 $element: $element,
34193 set: function(object, property) {
34194 object[property] = true;
34196 unset: function(object, property) {
34197 delete object[property];
34204 * @name ngModel.NgModelController#$setPristine
34207 * Sets the control to its pristine state.
34209 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
34210 * state (`ng-pristine` class). A model is considered to be pristine when the control
34211 * has not been changed from when first compiled.
34213 this.$setPristine = function() {
34214 ctrl.$dirty = false;
34215 ctrl.$pristine = true;
34216 $animate.removeClass($element, DIRTY_CLASS);
34217 $animate.addClass($element, PRISTINE_CLASS);
34222 * @name ngModel.NgModelController#$setDirty
34225 * Sets the control to its dirty state.
34227 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
34228 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
34229 * from when first compiled.
34231 this.$setDirty = function() {
34232 ctrl.$dirty = true;
34233 ctrl.$pristine = false;
34234 $animate.removeClass($element, PRISTINE_CLASS);
34235 $animate.addClass($element, DIRTY_CLASS);
34236 ctrl.$$parentForm.$setDirty();
34241 * @name ngModel.NgModelController#$setUntouched
34244 * Sets the control to its untouched state.
34246 * This method can be called to remove the `ng-touched` class and set the control to its
34247 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
34248 * by default, however this function can be used to restore that state if the model has
34249 * already been touched by the user.
34251 this.$setUntouched = function() {
34252 ctrl.$touched = false;
34253 ctrl.$untouched = true;
34254 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
34259 * @name ngModel.NgModelController#$setTouched
34262 * Sets the control to its touched state.
34264 * This method can be called to remove the `ng-untouched` class and set the control to its
34265 * touched state (`ng-touched` class). A model is considered to be touched when the user has
34266 * first focused the control element and then shifted focus away from the control (blur event).
34268 this.$setTouched = function() {
34269 ctrl.$touched = true;
34270 ctrl.$untouched = false;
34271 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
34276 * @name ngModel.NgModelController#$rollbackViewValue
34279 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
34280 * which may be caused by a pending debounced event or because the input is waiting for a some
34283 * If you have an input that uses `ng-model-options` to set up debounced events or events such
34284 * as blur you can have a situation where there is a period when the `$viewValue`
34285 * is out of synch with the ngModel's `$modelValue`.
34287 * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
34288 * programmatically before these debounced/future events have resolved/occurred, because Angular's
34289 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
34291 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
34292 * input which may have such events pending. This is important in order to make sure that the
34293 * input field will be updated with the new model value and any pending operations are cancelled.
34295 * <example name="ng-model-cancel-update" module="cancel-update-example">
34296 * <file name="app.js">
34297 * angular.module('cancel-update-example', [])
34299 * .controller('CancelUpdateController', ['$scope', function($scope) {
34300 * $scope.resetWithCancel = function(e) {
34301 * if (e.keyCode == 27) {
34302 * $scope.myForm.myInput1.$rollbackViewValue();
34303 * $scope.myValue = '';
34306 * $scope.resetWithoutCancel = function(e) {
34307 * if (e.keyCode == 27) {
34308 * $scope.myValue = '';
34313 * <file name="index.html">
34314 * <div ng-controller="CancelUpdateController">
34315 * <p>Try typing something in each input. See that the model only updates when you
34316 * blur off the input.
34318 * <p>Now see what happens if you start typing then press the Escape key</p>
34320 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
34321 * <p id="inputDescription1">With $rollbackViewValue()</p>
34322 * <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
34323 * ng-keydown="resetWithCancel($event)"><br/>
34324 * myValue: "{{ myValue }}"
34326 * <p id="inputDescription2">Without $rollbackViewValue()</p>
34327 * <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
34328 * ng-keydown="resetWithoutCancel($event)"><br/>
34329 * myValue: "{{ myValue }}"
34335 this.$rollbackViewValue = function() {
34336 $timeout.cancel(pendingDebounce);
34337 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
34343 * @name ngModel.NgModelController#$validate
34346 * Runs each of the registered validators (first synchronous validators and then
34347 * asynchronous validators).
34348 * If the validity changes to invalid, the model will be set to `undefined`,
34349 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
34350 * If the validity changes to valid, it will set the model to the last available valid
34351 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
34353 this.$validate = function() {
34354 // ignore $validate before model is initialized
34355 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
34359 var viewValue = ctrl.$$lastCommittedViewValue;
34360 // Note: we use the $$rawModelValue as $modelValue might have been
34361 // set to undefined during a view -> model update that found validation
34362 // errors. We can't parse the view here, since that could change
34363 // the model although neither viewValue nor the model on the scope changed
34364 var modelValue = ctrl.$$rawModelValue;
34366 var prevValid = ctrl.$valid;
34367 var prevModelValue = ctrl.$modelValue;
34369 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
34371 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
34372 // If there was no change in validity, don't update the model
34373 // This prevents changing an invalid modelValue to undefined
34374 if (!allowInvalid && prevValid !== allValid) {
34375 // Note: Don't check ctrl.$valid here, as we could have
34376 // external validators (e.g. calculated on the server),
34377 // that just call $setValidity and need the model value
34378 // to calculate their validity.
34379 ctrl.$modelValue = allValid ? modelValue : undefined;
34381 if (ctrl.$modelValue !== prevModelValue) {
34382 ctrl.$$writeModelToScope();
34389 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
34390 currentValidationRunId++;
34391 var localValidationRunId = currentValidationRunId;
34393 // check parser error
34394 if (!processParseErrors()) {
34395 validationDone(false);
34398 if (!processSyncValidators()) {
34399 validationDone(false);
34402 processAsyncValidators();
34404 function processParseErrors() {
34405 var errorKey = ctrl.$$parserName || 'parse';
34406 if (isUndefined(parserValid)) {
34407 setValidity(errorKey, null);
34409 if (!parserValid) {
34410 forEach(ctrl.$validators, function(v, name) {
34411 setValidity(name, null);
34413 forEach(ctrl.$asyncValidators, function(v, name) {
34414 setValidity(name, null);
34417 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
34418 setValidity(errorKey, parserValid);
34419 return parserValid;
34424 function processSyncValidators() {
34425 var syncValidatorsValid = true;
34426 forEach(ctrl.$validators, function(validator, name) {
34427 var result = validator(modelValue, viewValue);
34428 syncValidatorsValid = syncValidatorsValid && result;
34429 setValidity(name, result);
34431 if (!syncValidatorsValid) {
34432 forEach(ctrl.$asyncValidators, function(v, name) {
34433 setValidity(name, null);
34440 function processAsyncValidators() {
34441 var validatorPromises = [];
34442 var allValid = true;
34443 forEach(ctrl.$asyncValidators, function(validator, name) {
34444 var promise = validator(modelValue, viewValue);
34445 if (!isPromiseLike(promise)) {
34446 throw ngModelMinErr("$asyncValidators",
34447 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
34449 setValidity(name, undefined);
34450 validatorPromises.push(promise.then(function() {
34451 setValidity(name, true);
34452 }, function(error) {
34454 setValidity(name, false);
34457 if (!validatorPromises.length) {
34458 validationDone(true);
34460 $q.all(validatorPromises).then(function() {
34461 validationDone(allValid);
34466 function setValidity(name, isValid) {
34467 if (localValidationRunId === currentValidationRunId) {
34468 ctrl.$setValidity(name, isValid);
34472 function validationDone(allValid) {
34473 if (localValidationRunId === currentValidationRunId) {
34475 doneCallback(allValid);
34482 * @name ngModel.NgModelController#$commitViewValue
34485 * Commit a pending update to the `$modelValue`.
34487 * Updates may be pending by a debounced event or because the input is waiting for a some future
34488 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
34489 * usually handles calling this in response to input events.
34491 this.$commitViewValue = function() {
34492 var viewValue = ctrl.$viewValue;
34494 $timeout.cancel(pendingDebounce);
34496 // If the view value has not changed then we should just exit, except in the case where there is
34497 // a native validator on the element. In this case the validation state may have changed even though
34498 // the viewValue has stayed empty.
34499 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
34502 ctrl.$$lastCommittedViewValue = viewValue;
34505 if (ctrl.$pristine) {
34508 this.$$parseAndValidate();
34511 this.$$parseAndValidate = function() {
34512 var viewValue = ctrl.$$lastCommittedViewValue;
34513 var modelValue = viewValue;
34514 parserValid = isUndefined(modelValue) ? undefined : true;
34517 for (var i = 0; i < ctrl.$parsers.length; i++) {
34518 modelValue = ctrl.$parsers[i](modelValue);
34519 if (isUndefined(modelValue)) {
34520 parserValid = false;
34525 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
34526 // ctrl.$modelValue has not been touched yet...
34527 ctrl.$modelValue = ngModelGet($scope);
34529 var prevModelValue = ctrl.$modelValue;
34530 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
34531 ctrl.$$rawModelValue = modelValue;
34533 if (allowInvalid) {
34534 ctrl.$modelValue = modelValue;
34535 writeToModelIfNeeded();
34538 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
34539 // This can happen if e.g. $setViewValue is called from inside a parser
34540 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
34541 if (!allowInvalid) {
34542 // Note: Don't check ctrl.$valid here, as we could have
34543 // external validators (e.g. calculated on the server),
34544 // that just call $setValidity and need the model value
34545 // to calculate their validity.
34546 ctrl.$modelValue = allValid ? modelValue : undefined;
34547 writeToModelIfNeeded();
34551 function writeToModelIfNeeded() {
34552 if (ctrl.$modelValue !== prevModelValue) {
34553 ctrl.$$writeModelToScope();
34558 this.$$writeModelToScope = function() {
34559 ngModelSet($scope, ctrl.$modelValue);
34560 forEach(ctrl.$viewChangeListeners, function(listener) {
34564 $exceptionHandler(e);
34571 * @name ngModel.NgModelController#$setViewValue
34574 * Update the view value.
34576 * This method should be called when a control wants to change the view value; typically,
34577 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
34578 * directive calls it when the value of the input changes and {@link ng.directive:select select}
34579 * calls it when an option is selected.
34581 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
34582 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
34583 * value sent directly for processing, finally to be applied to `$modelValue` and then the
34584 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
34585 * in the `$viewChangeListeners` list, are called.
34587 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
34588 * and the `default` trigger is not listed, all those actions will remain pending until one of the
34589 * `updateOn` events is triggered on the DOM element.
34590 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
34591 * directive is used with a custom debounce for this particular event.
34592 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
34593 * is specified, once the timer runs out.
34595 * When used with standard inputs, the view value will always be a string (which is in some cases
34596 * parsed into another type, such as a `Date` object for `input[date]`.)
34597 * However, custom controls might also pass objects to this method. In this case, we should make
34598 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
34599 * perform a deep watch of objects, it only looks for a change of identity. If you only change
34600 * the property of the object then ngModel will not realise that the object has changed and
34601 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
34602 * not change properties of the copy once it has been passed to `$setViewValue`.
34603 * Otherwise you may cause the model value on the scope to change incorrectly.
34605 * <div class="alert alert-info">
34606 * In any case, the value passed to the method should always reflect the current value
34607 * of the control. For example, if you are calling `$setViewValue` for an input element,
34608 * you should pass the input DOM value. Otherwise, the control and the scope model become
34609 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
34610 * the control's DOM value in any way. If we want to change the control's DOM value
34611 * programmatically, we should update the `ngModel` scope expression. Its new value will be
34612 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
34613 * to update the DOM, and finally call `$validate` on it.
34616 * @param {*} value value from the view.
34617 * @param {string} trigger Event that triggered the update.
34619 this.$setViewValue = function(value, trigger) {
34620 ctrl.$viewValue = value;
34621 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
34622 ctrl.$$debounceViewValueCommit(trigger);
34626 this.$$debounceViewValueCommit = function(trigger) {
34627 var debounceDelay = 0,
34628 options = ctrl.$options,
34631 if (options && isDefined(options.debounce)) {
34632 debounce = options.debounce;
34633 if (isNumber(debounce)) {
34634 debounceDelay = debounce;
34635 } else if (isNumber(debounce[trigger])) {
34636 debounceDelay = debounce[trigger];
34637 } else if (isNumber(debounce['default'])) {
34638 debounceDelay = debounce['default'];
34642 $timeout.cancel(pendingDebounce);
34643 if (debounceDelay) {
34644 pendingDebounce = $timeout(function() {
34645 ctrl.$commitViewValue();
34647 } else if ($rootScope.$$phase) {
34648 ctrl.$commitViewValue();
34650 $scope.$apply(function() {
34651 ctrl.$commitViewValue();
34657 // Note: we cannot use a normal scope.$watch as we want to detect the following:
34658 // 1. scope value is 'a'
34659 // 2. user enters 'b'
34660 // 3. ng-change kicks in and reverts scope value to 'a'
34661 // -> scope value did not change since the last digest as
34662 // ng-change executes in apply phase
34663 // 4. view should be changed back to 'a'
34664 $scope.$watch(function ngModelWatch() {
34665 var modelValue = ngModelGet($scope);
34667 // if scope model value and ngModel value are out of sync
34668 // TODO(perf): why not move this to the action fn?
34669 if (modelValue !== ctrl.$modelValue &&
34670 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
34671 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
34673 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
34674 parserValid = undefined;
34676 var formatters = ctrl.$formatters,
34677 idx = formatters.length;
34679 var viewValue = modelValue;
34681 viewValue = formatters[idx](viewValue);
34683 if (ctrl.$viewValue !== viewValue) {
34684 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
34687 ctrl.$$runValidators(modelValue, viewValue, noop);
34704 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
34705 * property on the scope using {@link ngModel.NgModelController NgModelController},
34706 * which is created and exposed by this directive.
34708 * `ngModel` is responsible for:
34710 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
34712 * - Providing validation behavior (i.e. required, number, email, url).
34713 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
34714 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
34715 * - Registering the control with its parent {@link ng.directive:form form}.
34717 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
34718 * current scope. If the property doesn't already exist on this scope, it will be created
34719 * implicitly and added to the scope.
34721 * For best practices on using `ngModel`, see:
34723 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
34725 * For basic examples, how to use `ngModel`, see:
34727 * - {@link ng.directive:input input}
34728 * - {@link input[text] text}
34729 * - {@link input[checkbox] checkbox}
34730 * - {@link input[radio] radio}
34731 * - {@link input[number] number}
34732 * - {@link input[email] email}
34733 * - {@link input[url] url}
34734 * - {@link input[date] date}
34735 * - {@link input[datetime-local] datetime-local}
34736 * - {@link input[time] time}
34737 * - {@link input[month] month}
34738 * - {@link input[week] week}
34739 * - {@link ng.directive:select select}
34740 * - {@link ng.directive:textarea textarea}
34743 * The following CSS classes are added and removed on the associated input/select/textarea element
34744 * depending on the validity of the model.
34746 * - `ng-valid`: the model is valid
34747 * - `ng-invalid`: the model is invalid
34748 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
34749 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
34750 * - `ng-pristine`: the control hasn't been interacted with yet
34751 * - `ng-dirty`: the control has been interacted with
34752 * - `ng-touched`: the control has been blurred
34753 * - `ng-untouched`: the control hasn't been blurred
34754 * - `ng-pending`: any `$asyncValidators` are unfulfilled
34756 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
34758 * ## Animation Hooks
34760 * Animations within models are triggered when any of the associated CSS classes are added and removed
34761 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
34762 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
34763 * The animations that are triggered within ngModel are similar to how they work in ngClass and
34764 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
34766 * The following example shows a simple way to utilize CSS transitions to style an input element
34767 * that has been rendered as invalid after it has been validated:
34770 * //be sure to include ngAnimate as a module to hook into more
34771 * //advanced animations
34773 * transition:0.5s linear all;
34774 * background: white;
34776 * .my-input.ng-invalid {
34783 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
34784 <file name="index.html">
34786 angular.module('inputExample', [])
34787 .controller('ExampleController', ['$scope', function($scope) {
34793 transition:all linear 0.5s;
34794 background: transparent;
34796 .my-input.ng-invalid {
34801 <p id="inputDescription">
34802 Update input to see transitions when valid/invalid.
34803 Integer is a valid value.
34805 <form name="testForm" ng-controller="ExampleController">
34806 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
34807 aria-describedby="inputDescription" />
34812 * ## Binding to a getter/setter
34814 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
34815 * function that returns a representation of the model when called with zero arguments, and sets
34816 * the internal state of a model when called with an argument. It's sometimes useful to use this
34817 * for models that have an internal representation that's different from what the model exposes
34820 * <div class="alert alert-success">
34821 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
34822 * frequently than other parts of your code.
34825 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
34826 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
34827 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
34828 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
34830 * The following example shows how to use `ngModel` with a getter/setter:
34833 * <example name="ngModel-getter-setter" module="getterSetterExample">
34834 <file name="index.html">
34835 <div ng-controller="ExampleController">
34836 <form name="userForm">
34838 <input type="text" name="userName"
34839 ng-model="user.name"
34840 ng-model-options="{ getterSetter: true }" />
34843 <pre>user.name = <span ng-bind="user.name()"></span></pre>
34846 <file name="app.js">
34847 angular.module('getterSetterExample', [])
34848 .controller('ExampleController', ['$scope', function($scope) {
34849 var _name = 'Brian';
34851 name: function(newName) {
34852 // Note that newName can be undefined for two reasons:
34853 // 1. Because it is called as a getter and thus called with no arguments
34854 // 2. Because the property should actually be set to undefined. This happens e.g. if the
34855 // input is invalid
34856 return arguments.length ? (_name = newName) : _name;
34863 var ngModelDirective = ['$rootScope', function($rootScope) {
34866 require: ['ngModel', '^?form', '^?ngModelOptions'],
34867 controller: NgModelController,
34868 // Prelink needs to run before any input directive
34869 // so that we can set the NgModelOptions in NgModelController
34870 // before anyone else uses it.
34872 compile: function ngModelCompile(element) {
34873 // Setup initial state of the control
34874 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
34877 pre: function ngModelPreLink(scope, element, attr, ctrls) {
34878 var modelCtrl = ctrls[0],
34879 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
34881 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
34883 // notify others, especially parent forms
34884 formCtrl.$addControl(modelCtrl);
34886 attr.$observe('name', function(newValue) {
34887 if (modelCtrl.$name !== newValue) {
34888 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
34892 scope.$on('$destroy', function() {
34893 modelCtrl.$$parentForm.$removeControl(modelCtrl);
34896 post: function ngModelPostLink(scope, element, attr, ctrls) {
34897 var modelCtrl = ctrls[0];
34898 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
34899 element.on(modelCtrl.$options.updateOn, function(ev) {
34900 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
34904 element.on('blur', function(ev) {
34905 if (modelCtrl.$touched) return;
34907 if ($rootScope.$$phase) {
34908 scope.$evalAsync(modelCtrl.$setTouched);
34910 scope.$apply(modelCtrl.$setTouched);
34919 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
34923 * @name ngModelOptions
34926 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
34927 * events that will trigger a model update and/or a debouncing delay so that the actual update only
34928 * takes place when a timer expires; this timer will be reset after another change takes place.
34930 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
34931 * be different from the value in the actual model. This means that if you update the model you
34932 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
34933 * order to make sure it is synchronized with the model and that any debounced action is canceled.
34935 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
34936 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
34937 * important because `form` controllers are published to the related scope under the name in their
34938 * `name` attribute.
34940 * Any pending changes will take place immediately when an enclosing form is submitted via the
34941 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
34942 * to have access to the updated model.
34944 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
34946 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
34947 * - `updateOn`: string specifying which event should the input be bound to. You can set several
34948 * events using an space delimited list. There is a special event called `default` that
34949 * matches the default events belonging of the control.
34950 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
34951 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
34952 * custom value for each event. For example:
34953 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
34954 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
34955 * not validate correctly instead of the default behavior of setting the model to undefined.
34956 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
34957 `ngModel` as getters/setters.
34958 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
34959 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
34960 * continental US time zone abbreviations, but for general use, use a time zone offset, for
34961 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
34962 * If not specified, the timezone of the browser will be used.
34966 The following example shows how to override immediate updates. Changes on the inputs within the
34967 form will update the model only when the control loses focus (blur event). If `escape` key is
34968 pressed while the input field is focused, the value is reset to the value in the current model.
34970 <example name="ngModelOptions-directive-blur" module="optionsExample">
34971 <file name="index.html">
34972 <div ng-controller="ExampleController">
34973 <form name="userForm">
34975 <input type="text" name="userName"
34976 ng-model="user.name"
34977 ng-model-options="{ updateOn: 'blur' }"
34978 ng-keyup="cancel($event)" />
34981 <input type="text" ng-model="user.data" />
34984 <pre>user.name = <span ng-bind="user.name"></span></pre>
34985 <pre>user.data = <span ng-bind="user.data"></span></pre>
34988 <file name="app.js">
34989 angular.module('optionsExample', [])
34990 .controller('ExampleController', ['$scope', function($scope) {
34991 $scope.user = { name: 'John', data: '' };
34993 $scope.cancel = function(e) {
34994 if (e.keyCode == 27) {
34995 $scope.userForm.userName.$rollbackViewValue();
35000 <file name="protractor.js" type="protractor">
35001 var model = element(by.binding('user.name'));
35002 var input = element(by.model('user.name'));
35003 var other = element(by.model('user.data'));
35005 it('should allow custom events', function() {
35006 input.sendKeys(' Doe');
35008 expect(model.getText()).toEqual('John');
35010 expect(model.getText()).toEqual('John Doe');
35013 it('should $rollbackViewValue when model changes', function() {
35014 input.sendKeys(' Doe');
35015 expect(input.getAttribute('value')).toEqual('John Doe');
35016 input.sendKeys(protractor.Key.ESCAPE);
35017 expect(input.getAttribute('value')).toEqual('John');
35019 expect(model.getText()).toEqual('John');
35024 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
35025 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
35027 <example name="ngModelOptions-directive-debounce" module="optionsExample">
35028 <file name="index.html">
35029 <div ng-controller="ExampleController">
35030 <form name="userForm">
35032 <input type="text" name="userName"
35033 ng-model="user.name"
35034 ng-model-options="{ debounce: 1000 }" />
35036 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
35039 <pre>user.name = <span ng-bind="user.name"></span></pre>
35042 <file name="app.js">
35043 angular.module('optionsExample', [])
35044 .controller('ExampleController', ['$scope', function($scope) {
35045 $scope.user = { name: 'Igor' };
35050 This one shows how to bind to getter/setters:
35052 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
35053 <file name="index.html">
35054 <div ng-controller="ExampleController">
35055 <form name="userForm">
35057 <input type="text" name="userName"
35058 ng-model="user.name"
35059 ng-model-options="{ getterSetter: true }" />
35062 <pre>user.name = <span ng-bind="user.name()"></span></pre>
35065 <file name="app.js">
35066 angular.module('getterSetterExample', [])
35067 .controller('ExampleController', ['$scope', function($scope) {
35068 var _name = 'Brian';
35070 name: function(newName) {
35071 // Note that newName can be undefined for two reasons:
35072 // 1. Because it is called as a getter and thus called with no arguments
35073 // 2. Because the property should actually be set to undefined. This happens e.g. if the
35074 // input is invalid
35075 return arguments.length ? (_name = newName) : _name;
35082 var ngModelOptionsDirective = function() {
35085 controller: ['$scope', '$attrs', function($scope, $attrs) {
35087 this.$options = copy($scope.$eval($attrs.ngModelOptions));
35088 // Allow adding/overriding bound events
35089 if (isDefined(this.$options.updateOn)) {
35090 this.$options.updateOnDefault = false;
35091 // extract "default" pseudo-event from list of events that can trigger a model update
35092 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
35093 that.$options.updateOnDefault = true;
35097 this.$options.updateOnDefault = true;
35106 function addSetValidityMethod(context) {
35107 var ctrl = context.ctrl,
35108 $element = context.$element,
35111 unset = context.unset,
35112 $animate = context.$animate;
35114 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
35116 ctrl.$setValidity = setValidity;
35118 function setValidity(validationErrorKey, state, controller) {
35119 if (isUndefined(state)) {
35120 createAndSet('$pending', validationErrorKey, controller);
35122 unsetAndCleanup('$pending', validationErrorKey, controller);
35124 if (!isBoolean(state)) {
35125 unset(ctrl.$error, validationErrorKey, controller);
35126 unset(ctrl.$$success, validationErrorKey, controller);
35129 unset(ctrl.$error, validationErrorKey, controller);
35130 set(ctrl.$$success, validationErrorKey, controller);
35132 set(ctrl.$error, validationErrorKey, controller);
35133 unset(ctrl.$$success, validationErrorKey, controller);
35136 if (ctrl.$pending) {
35137 cachedToggleClass(PENDING_CLASS, true);
35138 ctrl.$valid = ctrl.$invalid = undefined;
35139 toggleValidationCss('', null);
35141 cachedToggleClass(PENDING_CLASS, false);
35142 ctrl.$valid = isObjectEmpty(ctrl.$error);
35143 ctrl.$invalid = !ctrl.$valid;
35144 toggleValidationCss('', ctrl.$valid);
35147 // re-read the state as the set/unset methods could have
35148 // combined state in ctrl.$error[validationError] (used for forms),
35149 // where setting/unsetting only increments/decrements the value,
35150 // and does not replace it.
35152 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
35153 combinedState = undefined;
35154 } else if (ctrl.$error[validationErrorKey]) {
35155 combinedState = false;
35156 } else if (ctrl.$$success[validationErrorKey]) {
35157 combinedState = true;
35159 combinedState = null;
35162 toggleValidationCss(validationErrorKey, combinedState);
35163 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
35166 function createAndSet(name, value, controller) {
35170 set(ctrl[name], value, controller);
35173 function unsetAndCleanup(name, value, controller) {
35175 unset(ctrl[name], value, controller);
35177 if (isObjectEmpty(ctrl[name])) {
35178 ctrl[name] = undefined;
35182 function cachedToggleClass(className, switchValue) {
35183 if (switchValue && !classCache[className]) {
35184 $animate.addClass($element, className);
35185 classCache[className] = true;
35186 } else if (!switchValue && classCache[className]) {
35187 $animate.removeClass($element, className);
35188 classCache[className] = false;
35192 function toggleValidationCss(validationErrorKey, isValid) {
35193 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
35195 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
35196 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
35200 function isObjectEmpty(obj) {
35202 for (var prop in obj) {
35203 if (obj.hasOwnProperty(prop)) {
35213 * @name ngNonBindable
35218 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
35219 * DOM element. This is useful if the element contains what appears to be Angular directives and
35220 * bindings but which should be ignored by Angular. This could be the case if you have a site that
35221 * displays snippets of code, for instance.
35226 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
35227 * but the one wrapped in `ngNonBindable` is left alone.
35231 <file name="index.html">
35232 <div>Normal: {{1 + 2}}</div>
35233 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
35235 <file name="protractor.js" type="protractor">
35236 it('should check ng-non-bindable', function() {
35237 expect(element(by.binding('1 + 2')).getText()).toContain('3');
35238 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
35243 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
35245 /* global jqLiteRemove */
35247 var ngOptionsMinErr = minErr('ngOptions');
35256 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
35257 * elements for the `<select>` element using the array or object obtained by evaluating the
35258 * `ngOptions` comprehension expression.
35260 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
35261 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
35262 * increasing speed by not creating a new scope for each repeated instance, as well as providing
35263 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
35264 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
35265 * to a non-string value. This is because an option element can only be bound to string values at
35268 * When an item in the `<select>` menu is selected, the array element or object property
35269 * represented by the selected option will be bound to the model identified by the `ngModel`
35272 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
35273 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
35274 * option. See example below for demonstration.
35276 * ## Complex Models (objects or collections)
35278 * By default, `ngModel` watches the model by reference, not value. This is important to know when
35279 * binding the select to a model that is an object or a collection.
35281 * One issue occurs if you want to preselect an option. For example, if you set
35282 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
35283 * because the objects are not identical. So by default, you should always reference the item in your collection
35284 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
35286 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
35287 * of the item not by reference, but by the result of the `track by` expression. For example, if your
35288 * collection items have an id property, you would `track by item.id`.
35290 * A different issue with objects or collections is that ngModel won't detect if an object property or
35291 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
35292 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
35293 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
35294 * has not changed identity, but only a property on the object or an item in the collection changes.
35296 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
35297 * if the model is an array). This means that changing a property deeper than the first level inside the
35298 * object/collection will not trigger a re-rendering.
35300 * ## `select` **`as`**
35302 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
35303 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
35304 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
35305 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
35308 * ### `select` **`as`** and **`track by`**
35310 * <div class="alert alert-warning">
35311 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
35314 * Given this array of items on the $scope:
35317 * $scope.items = [{
35320 * subItem: { name: 'aSubItem' }
35324 * subItem: { name: 'bSubItem' }
35331 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
35334 * $scope.selected = $scope.items[0];
35337 * but this will not work:
35340 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
35343 * $scope.selected = $scope.items[0].subItem;
35346 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
35347 * `items` array. Because the selected option has been set programmatically in the controller, the
35348 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
35349 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
35350 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
35351 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
35352 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
35355 * @param {string} ngModel Assignable angular expression to data-bind to.
35356 * @param {string=} name Property name of the form under which the control is published.
35357 * @param {string=} required The control is considered valid only if value is entered.
35358 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
35359 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
35360 * `required` when you want to data-bind to the `required` attribute.
35361 * @param {comprehension_expression=} ngOptions in one of the following forms:
35363 * * for array data sources:
35364 * * `label` **`for`** `value` **`in`** `array`
35365 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
35366 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
35367 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
35368 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
35369 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
35370 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
35371 * (for including a filter with `track by`)
35372 * * for object data sources:
35373 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
35374 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
35375 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
35376 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
35377 * * `select` **`as`** `label` **`group by`** `group`
35378 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
35379 * * `select` **`as`** `label` **`disable when`** `disable`
35380 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
35384 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
35385 * * `value`: local variable which will refer to each item in the `array` or each property value
35386 * of `object` during iteration.
35387 * * `key`: local variable which will refer to a property name in `object` during iteration.
35388 * * `label`: The result of this expression will be the label for `<option>` element. The
35389 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
35390 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
35391 * element. If not specified, `select` expression will default to `value`.
35392 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
35394 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
35395 * element. Return `true` to disable.
35396 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
35397 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
35398 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
35399 * even when the options are recreated (e.g. reloaded from the server).
35402 <example module="selectExample">
35403 <file name="index.html">
35405 angular.module('selectExample', [])
35406 .controller('ExampleController', ['$scope', function($scope) {
35408 {name:'black', shade:'dark'},
35409 {name:'white', shade:'light', notAnOption: true},
35410 {name:'red', shade:'dark'},
35411 {name:'blue', shade:'dark', notAnOption: true},
35412 {name:'yellow', shade:'light', notAnOption: false}
35414 $scope.myColor = $scope.colors[2]; // red
35417 <div ng-controller="ExampleController">
35419 <li ng-repeat="color in colors">
35420 <label>Name: <input ng-model="color.name"></label>
35421 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
35422 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
35425 <button ng-click="colors.push({})">add</button>
35429 <label>Color (null not allowed):
35430 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
35432 <label>Color (null allowed):
35433 <span class="nullable">
35434 <select ng-model="myColor" ng-options="color.name for color in colors">
35435 <option value="">-- choose color --</option>
35437 </span></label><br/>
35439 <label>Color grouped by shade:
35440 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
35444 <label>Color grouped by shade, with some disabled:
35445 <select ng-model="myColor"
35446 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
35452 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
35455 Currently selected: {{ {selected_color:myColor} }}
35456 <div style="border:solid 1px black; height:20px"
35457 ng-style="{'background-color':myColor.name}">
35461 <file name="protractor.js" type="protractor">
35462 it('should check ng-options', function() {
35463 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
35464 element.all(by.model('myColor')).first().click();
35465 element.all(by.css('select[ng-model="myColor"] option')).first().click();
35466 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
35467 element(by.css('.nullable select[ng-model="myColor"]')).click();
35468 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
35469 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
35475 // jshint maxlen: false
35476 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
35477 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]+?))?$/;
35478 // 1: value expression (valueFn)
35479 // 2: label expression (displayFn)
35480 // 3: group by expression (groupByFn)
35481 // 4: disable when expression (disableWhenFn)
35482 // 5: array item variable name
35483 // 6: object item key variable name
35484 // 7: object item value variable name
35485 // 8: collection expression
35486 // 9: track by expression
35487 // jshint maxlen: 100
35490 var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
35492 function parseOptionsExpression(optionsExp, selectElement, scope) {
35494 var match = optionsExp.match(NG_OPTIONS_REGEXP);
35496 throw ngOptionsMinErr('iexp',
35497 "Expected expression in form of " +
35498 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
35499 " but got '{0}'. Element: {1}",
35500 optionsExp, startingTag(selectElement));
35503 // Extract the parts from the ngOptions expression
35505 // The variable name for the value of the item in the collection
35506 var valueName = match[5] || match[7];
35507 // The variable name for the key of the item in the collection
35508 var keyName = match[6];
35510 // An expression that generates the viewValue for an option if there is a label expression
35511 var selectAs = / as /.test(match[0]) && match[1];
35512 // An expression that is used to track the id of each object in the options collection
35513 var trackBy = match[9];
35514 // An expression that generates the viewValue for an option if there is no label expression
35515 var valueFn = $parse(match[2] ? match[1] : valueName);
35516 var selectAsFn = selectAs && $parse(selectAs);
35517 var viewValueFn = selectAsFn || valueFn;
35518 var trackByFn = trackBy && $parse(trackBy);
35520 // Get the value by which we are going to track the option
35521 // if we have a trackFn then use that (passing scope and locals)
35522 // otherwise just hash the given viewValue
35523 var getTrackByValueFn = trackBy ?
35524 function(value, locals) { return trackByFn(scope, locals); } :
35525 function getHashOfValue(value) { return hashKey(value); };
35526 var getTrackByValue = function(value, key) {
35527 return getTrackByValueFn(value, getLocals(value, key));
35530 var displayFn = $parse(match[2] || match[1]);
35531 var groupByFn = $parse(match[3] || '');
35532 var disableWhenFn = $parse(match[4] || '');
35533 var valuesFn = $parse(match[8]);
35536 var getLocals = keyName ? function(value, key) {
35537 locals[keyName] = key;
35538 locals[valueName] = value;
35540 } : function(value) {
35541 locals[valueName] = value;
35546 function Option(selectValue, viewValue, label, group, disabled) {
35547 this.selectValue = selectValue;
35548 this.viewValue = viewValue;
35549 this.label = label;
35550 this.group = group;
35551 this.disabled = disabled;
35554 function getOptionValuesKeys(optionValues) {
35555 var optionValuesKeys;
35557 if (!keyName && isArrayLike(optionValues)) {
35558 optionValuesKeys = optionValues;
35560 // if object, extract keys, in enumeration order, unsorted
35561 optionValuesKeys = [];
35562 for (var itemKey in optionValues) {
35563 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
35564 optionValuesKeys.push(itemKey);
35568 return optionValuesKeys;
35573 getTrackByValue: getTrackByValue,
35574 getWatchables: $parse(valuesFn, function(optionValues) {
35575 // Create a collection of things that we would like to watch (watchedArray)
35576 // so that they can all be watched using a single $watchCollection
35577 // that only runs the handler once if anything changes
35578 var watchedArray = [];
35579 optionValues = optionValues || [];
35581 var optionValuesKeys = getOptionValuesKeys(optionValues);
35582 var optionValuesLength = optionValuesKeys.length;
35583 for (var index = 0; index < optionValuesLength; index++) {
35584 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
35585 var value = optionValues[key];
35587 var locals = getLocals(optionValues[key], key);
35588 var selectValue = getTrackByValueFn(optionValues[key], locals);
35589 watchedArray.push(selectValue);
35591 // Only need to watch the displayFn if there is a specific label expression
35592 if (match[2] || match[1]) {
35593 var label = displayFn(scope, locals);
35594 watchedArray.push(label);
35597 // Only need to watch the disableWhenFn if there is a specific disable expression
35599 var disableWhen = disableWhenFn(scope, locals);
35600 watchedArray.push(disableWhen);
35603 return watchedArray;
35606 getOptions: function() {
35608 var optionItems = [];
35609 var selectValueMap = {};
35611 // The option values were already computed in the `getWatchables` fn,
35612 // which must have been called to trigger `getOptions`
35613 var optionValues = valuesFn(scope) || [];
35614 var optionValuesKeys = getOptionValuesKeys(optionValues);
35615 var optionValuesLength = optionValuesKeys.length;
35617 for (var index = 0; index < optionValuesLength; index++) {
35618 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
35619 var value = optionValues[key];
35620 var locals = getLocals(value, key);
35621 var viewValue = viewValueFn(scope, locals);
35622 var selectValue = getTrackByValueFn(viewValue, locals);
35623 var label = displayFn(scope, locals);
35624 var group = groupByFn(scope, locals);
35625 var disabled = disableWhenFn(scope, locals);
35626 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
35628 optionItems.push(optionItem);
35629 selectValueMap[selectValue] = optionItem;
35633 items: optionItems,
35634 selectValueMap: selectValueMap,
35635 getOptionFromViewValue: function(value) {
35636 return selectValueMap[getTrackByValue(value)];
35638 getViewValueFromOption: function(option) {
35639 // If the viewValue could be an object that may be mutated by the application,
35640 // we need to make a copy and not return the reference to the value on the option.
35641 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
35649 // we can't just jqLite('<option>') since jqLite is not smart enough
35650 // to create it in <select> and IE barfs otherwise.
35651 var optionTemplate = document.createElement('option'),
35652 optGroupTemplate = document.createElement('optgroup');
35655 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
35657 // if ngModel is not defined, we don't need to do anything
35658 var ngModelCtrl = ctrls[1];
35659 if (!ngModelCtrl) return;
35661 var selectCtrl = ctrls[0];
35662 var multiple = attr.multiple;
35664 // The emptyOption allows the application developer to provide their own custom "empty"
35665 // option when the viewValue does not match any of the option values.
35667 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
35668 if (children[i].value === '') {
35669 emptyOption = children.eq(i);
35674 var providedEmptyOption = !!emptyOption;
35676 var unknownOption = jqLite(optionTemplate.cloneNode(false));
35677 unknownOption.val('?');
35680 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
35683 var renderEmptyOption = function() {
35684 if (!providedEmptyOption) {
35685 selectElement.prepend(emptyOption);
35687 selectElement.val('');
35688 emptyOption.prop('selected', true); // needed for IE
35689 emptyOption.attr('selected', true);
35692 var removeEmptyOption = function() {
35693 if (!providedEmptyOption) {
35694 emptyOption.remove();
35699 var renderUnknownOption = function() {
35700 selectElement.prepend(unknownOption);
35701 selectElement.val('?');
35702 unknownOption.prop('selected', true); // needed for IE
35703 unknownOption.attr('selected', true);
35706 var removeUnknownOption = function() {
35707 unknownOption.remove();
35710 // Update the controller methods for multiple selectable options
35713 selectCtrl.writeValue = function writeNgOptionsValue(value) {
35714 var option = options.getOptionFromViewValue(value);
35716 if (option && !option.disabled) {
35717 if (selectElement[0].value !== option.selectValue) {
35718 removeUnknownOption();
35719 removeEmptyOption();
35721 selectElement[0].value = option.selectValue;
35722 option.element.selected = true;
35723 option.element.setAttribute('selected', 'selected');
35726 if (value === null || providedEmptyOption) {
35727 removeUnknownOption();
35728 renderEmptyOption();
35730 removeEmptyOption();
35731 renderUnknownOption();
35736 selectCtrl.readValue = function readNgOptionsValue() {
35738 var selectedOption = options.selectValueMap[selectElement.val()];
35740 if (selectedOption && !selectedOption.disabled) {
35741 removeEmptyOption();
35742 removeUnknownOption();
35743 return options.getViewValueFromOption(selectedOption);
35748 // If we are using `track by` then we must watch the tracked value on the model
35749 // since ngModel only watches for object identity change
35750 if (ngOptions.trackBy) {
35752 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
35753 function() { ngModelCtrl.$render(); }
35759 ngModelCtrl.$isEmpty = function(value) {
35760 return !value || value.length === 0;
35764 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
35765 options.items.forEach(function(option) {
35766 option.element.selected = false;
35770 value.forEach(function(item) {
35771 var option = options.getOptionFromViewValue(item);
35772 if (option && !option.disabled) option.element.selected = true;
35778 selectCtrl.readValue = function readNgOptionsMultiple() {
35779 var selectedValues = selectElement.val() || [],
35782 forEach(selectedValues, function(value) {
35783 var option = options.selectValueMap[value];
35784 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
35790 // If we are using `track by` then we must watch these tracked values on the model
35791 // since ngModel only watches for object identity change
35792 if (ngOptions.trackBy) {
35794 scope.$watchCollection(function() {
35795 if (isArray(ngModelCtrl.$viewValue)) {
35796 return ngModelCtrl.$viewValue.map(function(value) {
35797 return ngOptions.getTrackByValue(value);
35801 ngModelCtrl.$render();
35808 if (providedEmptyOption) {
35810 // we need to remove it before calling selectElement.empty() because otherwise IE will
35811 // remove the label from the element. wtf?
35812 emptyOption.remove();
35814 // compile the element since there might be bindings in it
35815 $compile(emptyOption)(scope);
35817 // remove the class, which is added automatically because we recompile the element and it
35818 // becomes the compilation root
35819 emptyOption.removeClass('ng-scope');
35821 emptyOption = jqLite(optionTemplate.cloneNode(false));
35824 // We need to do this here to ensure that the options object is defined
35825 // when we first hit it in writeNgOptionsValue
35828 // We will re-render the option elements if the option values or labels change
35829 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
35831 // ------------------------------------------------------------------ //
35834 function updateOptionElement(option, element) {
35835 option.element = element;
35836 element.disabled = option.disabled;
35837 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
35838 // selects in certain circumstances when multiple selects are next to each other and display
35839 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
35840 // See https://github.com/angular/angular.js/issues/11314 for more info.
35841 // This is unfortunately untestable with unit / e2e tests
35842 if (option.label !== element.label) {
35843 element.label = option.label;
35844 element.textContent = option.label;
35846 if (option.value !== element.value) element.value = option.selectValue;
35849 function addOrReuseElement(parent, current, type, templateElement) {
35851 // Check whether we can reuse the next element
35852 if (current && lowercase(current.nodeName) === type) {
35853 // The next element is the right type so reuse it
35856 // The next element is not the right type so create a new one
35857 element = templateElement.cloneNode(false);
35859 // There are no more elements so just append it to the select
35860 parent.appendChild(element);
35862 // The next element is not a group so insert the new one
35863 parent.insertBefore(element, current);
35870 function removeExcessElements(current) {
35873 next = current.nextSibling;
35874 jqLiteRemove(current);
35880 function skipEmptyAndUnknownOptions(current) {
35881 var emptyOption_ = emptyOption && emptyOption[0];
35882 var unknownOption_ = unknownOption && unknownOption[0];
35884 // We cannot rely on the extracted empty option being the same as the compiled empty option,
35885 // because the compiled empty option might have been replaced by a comment because
35886 // it had an "element" transclusion directive on it (such as ngIf)
35887 if (emptyOption_ || unknownOption_) {
35889 (current === emptyOption_ ||
35890 current === unknownOption_ ||
35891 current.nodeType === NODE_TYPE_COMMENT ||
35892 current.value === '')) {
35893 current = current.nextSibling;
35900 function updateOptions() {
35902 var previousValue = options && selectCtrl.readValue();
35904 options = ngOptions.getOptions();
35907 var currentElement = selectElement[0].firstChild;
35909 // Ensure that the empty option is always there if it was explicitly provided
35910 if (providedEmptyOption) {
35911 selectElement.prepend(emptyOption);
35914 currentElement = skipEmptyAndUnknownOptions(currentElement);
35916 options.items.forEach(function updateOption(option) {
35921 if (option.group) {
35923 // This option is to live in a group
35924 // See if we have already created this group
35925 group = groupMap[option.group];
35929 // We have not already created this group
35930 groupElement = addOrReuseElement(selectElement[0],
35934 // Move to the next element
35935 currentElement = groupElement.nextSibling;
35937 // Update the label on the group element
35938 groupElement.label = option.group;
35940 // Store it for use later
35941 group = groupMap[option.group] = {
35942 groupElement: groupElement,
35943 currentOptionElement: groupElement.firstChild
35948 // So now we have a group for this option we add the option to the group
35949 optionElement = addOrReuseElement(group.groupElement,
35950 group.currentOptionElement,
35953 updateOptionElement(option, optionElement);
35954 // Move to the next element
35955 group.currentOptionElement = optionElement.nextSibling;
35959 // This option is not in a group
35960 optionElement = addOrReuseElement(selectElement[0],
35964 updateOptionElement(option, optionElement);
35965 // Move to the next element
35966 currentElement = optionElement.nextSibling;
35971 // Now remove all excess options and group
35972 Object.keys(groupMap).forEach(function(key) {
35973 removeExcessElements(groupMap[key].currentOptionElement);
35975 removeExcessElements(currentElement);
35977 ngModelCtrl.$render();
35979 // Check to see if the value has changed due to the update to the options
35980 if (!ngModelCtrl.$isEmpty(previousValue)) {
35981 var nextValue = selectCtrl.readValue();
35982 if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
35983 ngModelCtrl.$setViewValue(nextValue);
35984 ngModelCtrl.$render();
35994 require: ['select', '?ngModel'],
35996 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
35997 // Deactivate the SelectController.register method to prevent
35998 // option directives from accidentally registering themselves
35999 // (and unwanted $destroy handlers etc.)
36000 ctrls[0].registerOption = noop;
36002 post: ngOptionsPostLink
36009 * @name ngPluralize
36013 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
36014 * These rules are bundled with angular.js, but can be overridden
36015 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
36016 * by specifying the mappings between
36017 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
36018 * and the strings to be displayed.
36020 * # Plural categories and explicit number rules
36022 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
36023 * in Angular's default en-US locale: "one" and "other".
36025 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
36026 * any number that is not 1), an explicit number rule can only match one number. For example, the
36027 * explicit number rule for "3" matches the number 3. There are examples of plural categories
36028 * and explicit number rules throughout the rest of this documentation.
36030 * # Configuring ngPluralize
36031 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
36032 * You can also provide an optional attribute, `offset`.
36034 * The value of the `count` attribute can be either a string or an {@link guide/expression
36035 * Angular expression}; these are evaluated on the current scope for its bound value.
36037 * The `when` attribute specifies the mappings between plural categories and the actual
36038 * string to be displayed. The value of the attribute should be a JSON object.
36040 * The following example shows how to configure ngPluralize:
36043 * <ng-pluralize count="personCount"
36044 when="{'0': 'Nobody is viewing.',
36045 * 'one': '1 person is viewing.',
36046 * 'other': '{} people are viewing.'}">
36050 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
36051 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
36052 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
36053 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
36054 * show "a dozen people are viewing".
36056 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
36057 * into pluralized strings. In the previous example, Angular will replace `{}` with
36058 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
36059 * for <span ng-non-bindable>{{numberExpression}}</span>.
36061 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
36062 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
36064 * # Configuring ngPluralize with offset
36065 * The `offset` attribute allows further customization of pluralized text, which can result in
36066 * a better user experience. For example, instead of the message "4 people are viewing this document",
36067 * you might display "John, Kate and 2 others are viewing this document".
36068 * The offset attribute allows you to offset a number by any desired value.
36069 * Let's take a look at an example:
36072 * <ng-pluralize count="personCount" offset=2
36073 * when="{'0': 'Nobody is viewing.',
36074 * '1': '{{person1}} is viewing.',
36075 * '2': '{{person1}} and {{person2}} are viewing.',
36076 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
36077 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
36081 * Notice that we are still using two plural categories(one, other), but we added
36082 * three explicit number rules 0, 1 and 2.
36083 * When one person, perhaps John, views the document, "John is viewing" will be shown.
36084 * When three people view the document, no explicit number rule is found, so
36085 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
36086 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
36089 * Note that when you specify offsets, you must provide explicit number rules for
36090 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
36091 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
36092 * plural categories "one" and "other".
36094 * @param {string|expression} count The variable to be bound to.
36095 * @param {string} when The mapping between plural category to its corresponding strings.
36096 * @param {number=} offset Offset to deduct from the total number.
36099 <example module="pluralizeExample">
36100 <file name="index.html">
36102 angular.module('pluralizeExample', [])
36103 .controller('ExampleController', ['$scope', function($scope) {
36104 $scope.person1 = 'Igor';
36105 $scope.person2 = 'Misko';
36106 $scope.personCount = 1;
36109 <div ng-controller="ExampleController">
36110 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
36111 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
36112 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
36114 <!--- Example with simple pluralization rules for en locale --->
36116 <ng-pluralize count="personCount"
36117 when="{'0': 'Nobody is viewing.',
36118 'one': '1 person is viewing.',
36119 'other': '{} people are viewing.'}">
36120 </ng-pluralize><br>
36122 <!--- Example with offset --->
36124 <ng-pluralize count="personCount" offset=2
36125 when="{'0': 'Nobody is viewing.',
36126 '1': '{{person1}} is viewing.',
36127 '2': '{{person1}} and {{person2}} are viewing.',
36128 'one': '{{person1}}, {{person2}} and one other person are viewing.',
36129 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
36133 <file name="protractor.js" type="protractor">
36134 it('should show correct pluralized string', function() {
36135 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
36136 var withOffset = element.all(by.css('ng-pluralize')).get(1);
36137 var countInput = element(by.model('personCount'));
36139 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
36140 expect(withOffset.getText()).toEqual('Igor is viewing.');
36142 countInput.clear();
36143 countInput.sendKeys('0');
36145 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
36146 expect(withOffset.getText()).toEqual('Nobody is viewing.');
36148 countInput.clear();
36149 countInput.sendKeys('2');
36151 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
36152 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
36154 countInput.clear();
36155 countInput.sendKeys('3');
36157 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
36158 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
36160 countInput.clear();
36161 countInput.sendKeys('4');
36163 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
36164 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
36166 it('should show data-bound names', function() {
36167 var withOffset = element.all(by.css('ng-pluralize')).get(1);
36168 var personCount = element(by.model('personCount'));
36169 var person1 = element(by.model('person1'));
36170 var person2 = element(by.model('person2'));
36171 personCount.clear();
36172 personCount.sendKeys('4');
36174 person1.sendKeys('Di');
36176 person2.sendKeys('Vojta');
36177 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
36182 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
36184 IS_WHEN = /^when(Minus)?(.+)$/;
36187 link: function(scope, element, attr) {
36188 var numberExp = attr.count,
36189 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
36190 offset = attr.offset || 0,
36191 whens = scope.$eval(whenExp) || {},
36193 startSymbol = $interpolate.startSymbol(),
36194 endSymbol = $interpolate.endSymbol(),
36195 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
36196 watchRemover = angular.noop,
36199 forEach(attr, function(expression, attributeName) {
36200 var tmpMatch = IS_WHEN.exec(attributeName);
36202 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
36203 whens[whenKey] = element.attr(attr.$attr[attributeName]);
36206 forEach(whens, function(expression, key) {
36207 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
36211 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
36212 var count = parseFloat(newVal);
36213 var countIsNaN = isNaN(count);
36215 if (!countIsNaN && !(count in whens)) {
36216 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
36217 // Otherwise, check it against pluralization rules in $locale service.
36218 count = $locale.pluralCat(count - offset);
36221 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
36222 // In JS `NaN !== NaN`, so we have to exlicitly check.
36223 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
36225 var whenExpFn = whensExpFns[count];
36226 if (isUndefined(whenExpFn)) {
36227 if (newVal != null) {
36228 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
36230 watchRemover = noop;
36231 updateElementText();
36233 watchRemover = scope.$watch(whenExpFn, updateElementText);
36239 function updateElementText(newText) {
36240 element.text(newText || '');
36252 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
36253 * instance gets its own scope, where the given loop variable is set to the current collection item,
36254 * and `$index` is set to the item index or key.
36256 * Special properties are exposed on the local scope of each template instance, including:
36258 * | Variable | Type | Details |
36259 * |-----------|-----------------|-----------------------------------------------------------------------------|
36260 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
36261 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
36262 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
36263 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
36264 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
36265 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
36267 * <div class="alert alert-info">
36268 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
36269 * This may be useful when, for instance, nesting ngRepeats.
36273 * # Iterating over object properties
36275 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
36279 * <div ng-repeat="(key, value) in myObj"> ... </div>
36282 * You need to be aware that the JavaScript specification does not define the order of keys
36283 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
36284 * used to sort the keys alphabetically.)
36286 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
36287 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
36288 * keys in the order in which they were defined, although there are exceptions when keys are deleted
36289 * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
36291 * If this is not desired, the recommended workaround is to convert your object into an array
36292 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
36293 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
36294 * or implement a `$watch` on the object yourself.
36297 * # Tracking and Duplicates
36299 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
36300 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
36302 * * When an item is added, a new instance of the template is added to the DOM.
36303 * * When an item is removed, its template instance is removed from the DOM.
36304 * * When items are reordered, their respective templates are reordered in the DOM.
36306 * To minimize creation of DOM elements, `ngRepeat` uses a function
36307 * to "keep track" of all items in the collection and their corresponding DOM elements.
36308 * For example, if an item is added to the collection, ngRepeat will know that all other items
36309 * already have DOM elements, and will not re-render them.
36311 * The default tracking function (which tracks items by their identity) does not allow
36312 * duplicate items in arrays. This is because when there are duplicates, it is not possible
36313 * to maintain a one-to-one mapping between collection items and DOM elements.
36315 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
36316 * with your own using the `track by` expression.
36318 * For example, you may track items by the index of each item in the collection, using the
36319 * special scope property `$index`:
36321 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
36326 * You may also use arbitrary expressions in `track by`, including references to custom functions
36329 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
36334 * <div class="alert alert-success">
36335 * If you are working with objects that have an identifier property, you should track
36336 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
36337 * will not have to rebuild the DOM elements for items it has already rendered, even if the
36338 * JavaScript objects in the collection have been substituted for new ones. For large collections,
36339 * this signifincantly improves rendering performance. If you don't have a unique identifier,
36340 * `track by $index` can also provide a performance boost.
36343 * <div ng-repeat="model in collection track by model.id">
36348 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
36349 * `$id` function, which tracks items by their identity:
36351 * <div ng-repeat="obj in collection track by $id(obj)">
36356 * <div class="alert alert-warning">
36357 * **Note:** `track by` must always be the last expression:
36360 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
36365 * # Special repeat start and end points
36366 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
36367 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
36368 * 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)
36369 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
36371 * The example below makes use of this feature:
36373 * <header ng-repeat-start="item in items">
36374 * Header {{ item }}
36376 * <div class="body">
36379 * <footer ng-repeat-end>
36380 * Footer {{ item }}
36384 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
36389 * <div class="body">
36398 * <div class="body">
36406 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
36407 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
36410 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
36412 * **.leave** - when an item is removed from the list or when an item is filtered out
36414 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
36419 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
36420 * formats are currently supported:
36422 * * `variable in expression` – where variable is the user defined loop variable and `expression`
36423 * is a scope expression giving the collection to enumerate.
36425 * For example: `album in artist.albums`.
36427 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
36428 * and `expression` is the scope expression giving the collection to enumerate.
36430 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
36432 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
36433 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
36434 * is specified, ng-repeat associates elements by identity. It is an error to have
36435 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
36436 * mapped to the same DOM element, which is not possible.)
36438 * Note that the tracking expression must come last, after any filters, and the alias expression.
36440 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
36441 * will be associated by item identity in the array.
36443 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
36444 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
36445 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
36446 * element in the same way in the DOM.
36448 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
36449 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
36450 * property is same.
36452 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
36453 * to items in conjunction with a tracking expression.
36455 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
36456 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
36457 * when a filter is active on the repeater, but the filtered result set is empty.
36459 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
36460 * the items have been processed through the filter.
36462 * 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
36463 * (and not as operator, inside an expression).
36465 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
36468 * This example initializes the scope to a list of names and
36469 * then uses `ngRepeat` to display every person:
36470 <example module="ngAnimate" deps="angular-animate.js" animations="true">
36471 <file name="index.html">
36472 <div ng-init="friends = [
36473 {name:'John', age:25, gender:'boy'},
36474 {name:'Jessie', age:30, gender:'girl'},
36475 {name:'Johanna', age:28, gender:'girl'},
36476 {name:'Joy', age:15, gender:'girl'},
36477 {name:'Mary', age:28, gender:'girl'},
36478 {name:'Peter', age:95, gender:'boy'},
36479 {name:'Sebastian', age:50, gender:'boy'},
36480 {name:'Erika', age:27, gender:'girl'},
36481 {name:'Patrick', age:40, gender:'boy'},
36482 {name:'Samantha', age:60, gender:'girl'}
36484 I have {{friends.length}} friends. They are:
36485 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
36486 <ul class="example-animate-container">
36487 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
36488 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
36490 <li class="animate-repeat" ng-if="results.length == 0">
36491 <strong>No results found...</strong>
36496 <file name="animations.css">
36497 .example-animate-container {
36499 border:1px solid black;
36508 box-sizing:border-box;
36511 .animate-repeat.ng-move,
36512 .animate-repeat.ng-enter,
36513 .animate-repeat.ng-leave {
36514 transition:all linear 0.5s;
36517 .animate-repeat.ng-leave.ng-leave-active,
36518 .animate-repeat.ng-move,
36519 .animate-repeat.ng-enter {
36524 .animate-repeat.ng-leave,
36525 .animate-repeat.ng-move.ng-move-active,
36526 .animate-repeat.ng-enter.ng-enter-active {
36531 <file name="protractor.js" type="protractor">
36532 var friends = element.all(by.repeater('friend in friends'));
36534 it('should render initial data set', function() {
36535 expect(friends.count()).toBe(10);
36536 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
36537 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
36538 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
36539 expect(element(by.binding('friends.length')).getText())
36540 .toMatch("I have 10 friends. They are:");
36543 it('should update repeater when filter predicate changes', function() {
36544 expect(friends.count()).toBe(10);
36546 element(by.model('q')).sendKeys('ma');
36548 expect(friends.count()).toBe(2);
36549 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
36550 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
36555 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
36556 var NG_REMOVED = '$$NG_REMOVED';
36557 var ngRepeatMinErr = minErr('ngRepeat');
36559 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
36560 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
36561 scope[valueIdentifier] = value;
36562 if (keyIdentifier) scope[keyIdentifier] = key;
36563 scope.$index = index;
36564 scope.$first = (index === 0);
36565 scope.$last = (index === (arrayLength - 1));
36566 scope.$middle = !(scope.$first || scope.$last);
36567 // jshint bitwise: false
36568 scope.$odd = !(scope.$even = (index&1) === 0);
36569 // jshint bitwise: true
36572 var getBlockStart = function(block) {
36573 return block.clone[0];
36576 var getBlockEnd = function(block) {
36577 return block.clone[block.clone.length - 1];
36583 multiElement: true,
36584 transclude: 'element',
36588 compile: function ngRepeatCompile($element, $attr) {
36589 var expression = $attr.ngRepeat;
36590 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
36592 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*$/);
36595 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
36599 var lhs = match[1];
36600 var rhs = match[2];
36601 var aliasAs = match[3];
36602 var trackByExp = match[4];
36604 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
36607 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
36610 var valueIdentifier = match[3] || match[1];
36611 var keyIdentifier = match[2];
36613 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
36614 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
36615 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
36619 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
36620 var hashFnLocals = {$id: hashKey};
36623 trackByExpGetter = $parse(trackByExp);
36625 trackByIdArrayFn = function(key, value) {
36626 return hashKey(value);
36628 trackByIdObjFn = function(key) {
36633 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
36635 if (trackByExpGetter) {
36636 trackByIdExpFn = function(key, value, index) {
36637 // assign key, value, and $index to the locals so that they can be used in hash functions
36638 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
36639 hashFnLocals[valueIdentifier] = value;
36640 hashFnLocals.$index = index;
36641 return trackByExpGetter($scope, hashFnLocals);
36645 // Store a list of elements from previous run. This is a hash where key is the item from the
36646 // iterator, and the value is objects with following properties.
36647 // - scope: bound scope
36648 // - element: previous element.
36649 // - index: position
36651 // We are using no-proto object so that we don't need to guard against inherited props via
36653 var lastBlockMap = createMap();
36656 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
36658 previousNode = $element[0], // node that cloned nodes should be inserted after
36659 // initialized to the comment node anchor
36661 // Same as lastBlockMap but it has the current state. It will become the
36662 // lastBlockMap on the next iteration.
36663 nextBlockMap = createMap(),
36665 key, value, // key/value of iteration
36669 block, // last object information {scope, element, id}
36674 $scope[aliasAs] = collection;
36677 if (isArrayLike(collection)) {
36678 collectionKeys = collection;
36679 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
36681 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
36682 // if object, extract keys, in enumeration order, unsorted
36683 collectionKeys = [];
36684 for (var itemKey in collection) {
36685 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
36686 collectionKeys.push(itemKey);
36691 collectionLength = collectionKeys.length;
36692 nextBlockOrder = new Array(collectionLength);
36694 // locate existing items
36695 for (index = 0; index < collectionLength; index++) {
36696 key = (collection === collectionKeys) ? index : collectionKeys[index];
36697 value = collection[key];
36698 trackById = trackByIdFn(key, value, index);
36699 if (lastBlockMap[trackById]) {
36700 // found previously seen block
36701 block = lastBlockMap[trackById];
36702 delete lastBlockMap[trackById];
36703 nextBlockMap[trackById] = block;
36704 nextBlockOrder[index] = block;
36705 } else if (nextBlockMap[trackById]) {
36706 // if collision detected. restore lastBlockMap and throw an error
36707 forEach(nextBlockOrder, function(block) {
36708 if (block && block.scope) lastBlockMap[block.id] = block;
36710 throw ngRepeatMinErr('dupes',
36711 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
36712 expression, trackById, value);
36714 // new never before seen block
36715 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
36716 nextBlockMap[trackById] = true;
36720 // remove leftover items
36721 for (var blockKey in lastBlockMap) {
36722 block = lastBlockMap[blockKey];
36723 elementsToRemove = getBlockNodes(block.clone);
36724 $animate.leave(elementsToRemove);
36725 if (elementsToRemove[0].parentNode) {
36726 // if the element was not removed yet because of pending animation, mark it as deleted
36727 // so that we can ignore it later
36728 for (index = 0, length = elementsToRemove.length; index < length; index++) {
36729 elementsToRemove[index][NG_REMOVED] = true;
36732 block.scope.$destroy();
36735 // we are not using forEach for perf reasons (trying to avoid #call)
36736 for (index = 0; index < collectionLength; index++) {
36737 key = (collection === collectionKeys) ? index : collectionKeys[index];
36738 value = collection[key];
36739 block = nextBlockOrder[index];
36742 // if we have already seen this object, then we need to reuse the
36743 // associated scope/element
36745 nextNode = previousNode;
36747 // skip nodes that are already pending removal via leave animation
36749 nextNode = nextNode.nextSibling;
36750 } while (nextNode && nextNode[NG_REMOVED]);
36752 if (getBlockStart(block) != nextNode) {
36753 // existing item which got moved
36754 $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
36756 previousNode = getBlockEnd(block);
36757 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
36759 // new item which we don't know about
36760 $transclude(function ngRepeatTransclude(clone, scope) {
36761 block.scope = scope;
36762 // http://jsperf.com/clone-vs-createcomment
36763 var endNode = ngRepeatEndComment.cloneNode(false);
36764 clone[clone.length++] = endNode;
36766 // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
36767 $animate.enter(clone, null, jqLite(previousNode));
36768 previousNode = endNode;
36769 // Note: We only need the first/last node of the cloned nodes.
36770 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
36771 // by a directive with templateUrl when its template arrives.
36772 block.clone = clone;
36773 nextBlockMap[block.id] = block;
36774 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
36778 lastBlockMap = nextBlockMap;
36785 var NG_HIDE_CLASS = 'ng-hide';
36786 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
36793 * The `ngShow` directive shows or hides the given HTML element based on the expression
36794 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
36795 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
36796 * in AngularJS and sets the display style to none (using an !important flag).
36797 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
36800 * <!-- when $scope.myValue is truthy (element is visible) -->
36801 * <div ng-show="myValue"></div>
36803 * <!-- when $scope.myValue is falsy (element is hidden) -->
36804 * <div ng-show="myValue" class="ng-hide"></div>
36807 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
36808 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
36809 * from the element causing the element not to appear hidden.
36811 * ## Why is !important used?
36813 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
36814 * can be easily overridden by heavier selectors. For example, something as simple
36815 * as changing the display style on a HTML list item would make hidden elements appear visible.
36816 * This also becomes a bigger issue when dealing with CSS frameworks.
36818 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
36819 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
36820 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
36822 * ### Overriding `.ng-hide`
36824 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
36825 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
36826 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
36827 * with extra animation classes that can be added.
36830 * .ng-hide:not(.ng-hide-animate) {
36831 * /* this is just another form of hiding an element */
36832 * display: block!important;
36833 * position: absolute;
36839 * By default you don't need to override in CSS anything and the animations will work around the display style.
36841 * ## A note about animations with `ngShow`
36843 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
36844 * is true and false. This system works like the animation system present with ngClass except that
36845 * you must also include the !important flag to override the display property
36846 * so that you can perform an animation when the element is hidden during the time of the animation.
36850 * //a working example can be found at the bottom of this page
36852 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
36853 * /* this is required as of 1.3x to properly
36854 * apply all styling in a show/hide animation */
36855 * transition: 0s linear all;
36858 * .my-element.ng-hide-add-active,
36859 * .my-element.ng-hide-remove-active {
36860 * /* the transition is defined in the active class */
36861 * transition: 1s linear all;
36864 * .my-element.ng-hide-add { ... }
36865 * .my-element.ng-hide-add.ng-hide-add-active { ... }
36866 * .my-element.ng-hide-remove { ... }
36867 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
36870 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
36871 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
36874 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
36875 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
36878 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
36879 * then the element is shown or hidden respectively.
36882 <example module="ngAnimate" deps="angular-animate.js" animations="true">
36883 <file name="index.html">
36884 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
36887 <div class="check-element animate-show" ng-show="checked">
36888 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
36893 <div class="check-element animate-show" ng-hide="checked">
36894 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
36898 <file name="glyphicons.css">
36899 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
36901 <file name="animations.css">
36906 border: 1px solid black;
36910 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
36911 transition: all linear 0.5s;
36914 .animate-show.ng-hide {
36922 border: 1px solid black;
36926 <file name="protractor.js" type="protractor">
36927 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
36928 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
36930 it('should check ng-show / ng-hide', function() {
36931 expect(thumbsUp.isDisplayed()).toBeFalsy();
36932 expect(thumbsDown.isDisplayed()).toBeTruthy();
36934 element(by.model('checked')).click();
36936 expect(thumbsUp.isDisplayed()).toBeTruthy();
36937 expect(thumbsDown.isDisplayed()).toBeFalsy();
36942 var ngShowDirective = ['$animate', function($animate) {
36945 multiElement: true,
36946 link: function(scope, element, attr) {
36947 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
36948 // we're adding a temporary, animation-specific class for ng-hide since this way
36949 // we can control when the element is actually displayed on screen without having
36950 // to have a global/greedy CSS selector that breaks when other animations are run.
36951 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
36952 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
36953 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
36967 * The `ngHide` directive shows or hides the given HTML element based on the expression
36968 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
36969 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
36970 * in AngularJS and sets the display style to none (using an !important flag).
36971 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
36974 * <!-- when $scope.myValue is truthy (element is hidden) -->
36975 * <div ng-hide="myValue" class="ng-hide"></div>
36977 * <!-- when $scope.myValue is falsy (element is visible) -->
36978 * <div ng-hide="myValue"></div>
36981 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
36982 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
36983 * from the element causing the element not to appear hidden.
36985 * ## Why is !important used?
36987 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
36988 * can be easily overridden by heavier selectors. For example, something as simple
36989 * as changing the display style on a HTML list item would make hidden elements appear visible.
36990 * This also becomes a bigger issue when dealing with CSS frameworks.
36992 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
36993 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
36994 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
36996 * ### Overriding `.ng-hide`
36998 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
36999 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
37004 * /* this is just another form of hiding an element */
37005 * display: block!important;
37006 * position: absolute;
37012 * By default you don't need to override in CSS anything and the animations will work around the display style.
37014 * ## A note about animations with `ngHide`
37016 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
37017 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
37018 * CSS class is added and removed for you instead of your own CSS class.
37022 * //a working example can be found at the bottom of this page
37024 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
37025 * transition: 0.5s linear all;
37028 * .my-element.ng-hide-add { ... }
37029 * .my-element.ng-hide-add.ng-hide-add-active { ... }
37030 * .my-element.ng-hide-remove { ... }
37031 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
37034 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
37035 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
37038 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
37039 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
37042 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
37043 * the element is shown or hidden respectively.
37046 <example module="ngAnimate" deps="angular-animate.js" animations="true">
37047 <file name="index.html">
37048 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
37051 <div class="check-element animate-hide" ng-show="checked">
37052 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
37057 <div class="check-element animate-hide" ng-hide="checked">
37058 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
37062 <file name="glyphicons.css">
37063 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
37065 <file name="animations.css">
37067 transition: all linear 0.5s;
37071 border: 1px solid black;
37075 .animate-hide.ng-hide {
37083 border: 1px solid black;
37087 <file name="protractor.js" type="protractor">
37088 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
37089 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
37091 it('should check ng-show / ng-hide', function() {
37092 expect(thumbsUp.isDisplayed()).toBeFalsy();
37093 expect(thumbsDown.isDisplayed()).toBeTruthy();
37095 element(by.model('checked')).click();
37097 expect(thumbsUp.isDisplayed()).toBeTruthy();
37098 expect(thumbsDown.isDisplayed()).toBeFalsy();
37103 var ngHideDirective = ['$animate', function($animate) {
37106 multiElement: true,
37107 link: function(scope, element, attr) {
37108 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
37109 // The comment inside of the ngShowDirective explains why we add and
37110 // remove a temporary class for the show/hide animation
37111 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
37112 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
37125 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
37128 * @param {expression} ngStyle
37130 * {@link guide/expression Expression} which evals to an
37131 * object whose keys are CSS style names and values are corresponding values for those CSS
37134 * Since some CSS style names are not valid keys for an object, they must be quoted.
37135 * See the 'background-color' style in the example below.
37139 <file name="index.html">
37140 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
37141 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
37142 <input type="button" value="clear" ng-click="myStyle={}">
37144 <span ng-style="myStyle">Sample Text</span>
37145 <pre>myStyle={{myStyle}}</pre>
37147 <file name="style.css">
37152 <file name="protractor.js" type="protractor">
37153 var colorSpan = element(by.css('span'));
37155 it('should check ng-style', function() {
37156 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
37157 element(by.css('input[value=\'set color\']')).click();
37158 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
37159 element(by.css('input[value=clear]')).click();
37160 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
37165 var ngStyleDirective = ngDirective(function(scope, element, attr) {
37166 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
37167 if (oldStyles && (newStyles !== oldStyles)) {
37168 forEach(oldStyles, function(val, style) { element.css(style, '');});
37170 if (newStyles) element.css(newStyles);
37180 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
37181 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
37182 * as specified in the template.
37184 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
37185 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
37186 * matches the value obtained from the evaluated expression. In other words, you define a container element
37187 * (where you place the directive), place an expression on the **`on="..."` attribute**
37188 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
37189 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
37190 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
37191 * attribute is displayed.
37193 * <div class="alert alert-info">
37194 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
37195 * as literal string values to match against.
37196 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
37197 * value of the expression `$scope.someVal`.
37201 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
37202 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
37207 * <ANY ng-switch="expression">
37208 * <ANY ng-switch-when="matchValue1">...</ANY>
37209 * <ANY ng-switch-when="matchValue2">...</ANY>
37210 * <ANY ng-switch-default>...</ANY>
37217 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
37218 * On child elements add:
37220 * * `ngSwitchWhen`: the case statement to match against. If match then this
37221 * case will be displayed. If the same match appears multiple times, all the
37222 * elements will be displayed.
37223 * * `ngSwitchDefault`: the default case when no other case match. If there
37224 * are multiple default cases, all of them will be displayed when no other
37229 <example module="switchExample" deps="angular-animate.js" animations="true">
37230 <file name="index.html">
37231 <div ng-controller="ExampleController">
37232 <select ng-model="selection" ng-options="item for item in items">
37234 <code>selection={{selection}}</code>
37236 <div class="animate-switch-container"
37237 ng-switch on="selection">
37238 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
37239 <div class="animate-switch" ng-switch-when="home">Home Span</div>
37240 <div class="animate-switch" ng-switch-default>default</div>
37244 <file name="script.js">
37245 angular.module('switchExample', ['ngAnimate'])
37246 .controller('ExampleController', ['$scope', function($scope) {
37247 $scope.items = ['settings', 'home', 'other'];
37248 $scope.selection = $scope.items[0];
37251 <file name="animations.css">
37252 .animate-switch-container {
37255 border:1px solid black;
37264 .animate-switch.ng-animate {
37265 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
37274 .animate-switch.ng-leave.ng-leave-active,
37275 .animate-switch.ng-enter {
37278 .animate-switch.ng-leave,
37279 .animate-switch.ng-enter.ng-enter-active {
37283 <file name="protractor.js" type="protractor">
37284 var switchElem = element(by.css('[ng-switch]'));
37285 var select = element(by.model('selection'));
37287 it('should start in settings', function() {
37288 expect(switchElem.getText()).toMatch(/Settings Div/);
37290 it('should change to home', function() {
37291 select.all(by.css('option')).get(1).click();
37292 expect(switchElem.getText()).toMatch(/Home Span/);
37294 it('should select default', function() {
37295 select.all(by.css('option')).get(2).click();
37296 expect(switchElem.getText()).toMatch(/default/);
37301 var ngSwitchDirective = ['$animate', function($animate) {
37303 require: 'ngSwitch',
37305 // asks for $scope to fool the BC controller module
37306 controller: ['$scope', function ngSwitchController() {
37309 link: function(scope, element, attr, ngSwitchController) {
37310 var watchExpr = attr.ngSwitch || attr.on,
37311 selectedTranscludes = [],
37312 selectedElements = [],
37313 previousLeaveAnimations = [],
37314 selectedScopes = [];
37316 var spliceFactory = function(array, index) {
37317 return function() { array.splice(index, 1); };
37320 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
37322 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
37323 $animate.cancel(previousLeaveAnimations[i]);
37325 previousLeaveAnimations.length = 0;
37327 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
37328 var selected = getBlockNodes(selectedElements[i].clone);
37329 selectedScopes[i].$destroy();
37330 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
37331 promise.then(spliceFactory(previousLeaveAnimations, i));
37334 selectedElements.length = 0;
37335 selectedScopes.length = 0;
37337 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
37338 forEach(selectedTranscludes, function(selectedTransclude) {
37339 selectedTransclude.transclude(function(caseElement, selectedScope) {
37340 selectedScopes.push(selectedScope);
37341 var anchor = selectedTransclude.element;
37342 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
37343 var block = { clone: caseElement };
37345 selectedElements.push(block);
37346 $animate.enter(caseElement, anchor.parent(), anchor);
37355 var ngSwitchWhenDirective = ngDirective({
37356 transclude: 'element',
37358 require: '^ngSwitch',
37359 multiElement: true,
37360 link: function(scope, element, attrs, ctrl, $transclude) {
37361 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
37362 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
37366 var ngSwitchDefaultDirective = ngDirective({
37367 transclude: 'element',
37369 require: '^ngSwitch',
37370 multiElement: true,
37371 link: function(scope, element, attr, ctrl, $transclude) {
37372 ctrl.cases['?'] = (ctrl.cases['?'] || []);
37373 ctrl.cases['?'].push({ transclude: $transclude, element: element });
37379 * @name ngTransclude
37383 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
37385 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
37390 <example module="transcludeExample">
37391 <file name="index.html">
37393 angular.module('transcludeExample', [])
37394 .directive('pane', function(){
37398 scope: { title:'@' },
37399 template: '<div style="border: 1px solid black;">' +
37400 '<div style="background-color: gray">{{title}}</div>' +
37401 '<ng-transclude></ng-transclude>' +
37405 .controller('ExampleController', ['$scope', function($scope) {
37406 $scope.title = 'Lorem Ipsum';
37407 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
37410 <div ng-controller="ExampleController">
37411 <input ng-model="title" aria-label="title"> <br/>
37412 <textarea ng-model="text" aria-label="text"></textarea> <br/>
37413 <pane title="{{title}}">{{text}}</pane>
37416 <file name="protractor.js" type="protractor">
37417 it('should have transcluded', function() {
37418 var titleElement = element(by.model('title'));
37419 titleElement.clear();
37420 titleElement.sendKeys('TITLE');
37421 var textElement = element(by.model('text'));
37422 textElement.clear();
37423 textElement.sendKeys('TEXT');
37424 expect(element(by.binding('title')).getText()).toEqual('TITLE');
37425 expect(element(by.binding('text')).getText()).toEqual('TEXT');
37431 var ngTranscludeDirective = ngDirective({
37433 link: function($scope, $element, $attrs, controller, $transclude) {
37434 if (!$transclude) {
37435 throw minErr('ngTransclude')('orphan',
37436 'Illegal use of ngTransclude directive in the template! ' +
37437 'No parent directive that requires a transclusion found. ' +
37439 startingTag($element));
37442 $transclude(function(clone) {
37444 $element.append(clone);
37455 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
37456 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
37457 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
37458 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
37459 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
37461 * @param {string} type Must be set to `'text/ng-template'`.
37462 * @param {string} id Cache name of the template.
37466 <file name="index.html">
37467 <script type="text/ng-template" id="/tpl.html">
37468 Content of the template.
37471 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
37472 <div id="tpl-content" ng-include src="currentTpl"></div>
37474 <file name="protractor.js" type="protractor">
37475 it('should load template defined inside script tag', function() {
37476 element(by.css('#tpl-link')).click();
37477 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
37482 var scriptDirective = ['$templateCache', function($templateCache) {
37486 compile: function(element, attr) {
37487 if (attr.type == 'text/ng-template') {
37488 var templateUrl = attr.id,
37489 text = element[0].text;
37491 $templateCache.put(templateUrl, text);
37497 var noopNgModelController = { $setViewValue: noop, $render: noop };
37499 function chromeHack(optionElement) {
37500 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
37501 // Adding an <option selected="selected"> element to a <select required="required"> should
37502 // automatically select the new element
37503 if (optionElement[0].hasAttribute('selected')) {
37504 optionElement[0].selected = true;
37510 * @name select.SelectController
37512 * The controller for the `<select>` directive. This provides support for reading
37513 * and writing the selected value(s) of the control and also coordinates dynamically
37514 * added `<option>` elements, perhaps by an `ngRepeat` directive.
37516 var SelectController =
37517 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
37520 optionsMap = new HashMap();
37522 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
37523 self.ngModelCtrl = noopNgModelController;
37525 // The "unknown" option is one that is prepended to the list if the viewValue
37526 // does not match any of the options. When it is rendered the value of the unknown
37527 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
37529 // We can't just jqLite('<option>') since jqLite is not smart enough
37530 // to create it in <select> and IE barfs otherwise.
37531 self.unknownOption = jqLite(document.createElement('option'));
37532 self.renderUnknownOption = function(val) {
37533 var unknownVal = '? ' + hashKey(val) + ' ?';
37534 self.unknownOption.val(unknownVal);
37535 $element.prepend(self.unknownOption);
37536 $element.val(unknownVal);
37539 $scope.$on('$destroy', function() {
37540 // disable unknown option so that we don't do work when the whole select is being destroyed
37541 self.renderUnknownOption = noop;
37544 self.removeUnknownOption = function() {
37545 if (self.unknownOption.parent()) self.unknownOption.remove();
37549 // Read the value of the select control, the implementation of this changes depending
37550 // upon whether the select can have multiple values and whether ngOptions is at work.
37551 self.readValue = function readSingleValue() {
37552 self.removeUnknownOption();
37553 return $element.val();
37557 // Write the value to the select control, the implementation of this changes depending
37558 // upon whether the select can have multiple values and whether ngOptions is at work.
37559 self.writeValue = function writeSingleValue(value) {
37560 if (self.hasOption(value)) {
37561 self.removeUnknownOption();
37562 $element.val(value);
37563 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
37565 if (value == null && self.emptyOption) {
37566 self.removeUnknownOption();
37569 self.renderUnknownOption(value);
37575 // Tell the select control that an option, with the given value, has been added
37576 self.addOption = function(value, element) {
37577 assertNotHasOwnProperty(value, '"option value"');
37578 if (value === '') {
37579 self.emptyOption = element;
37581 var count = optionsMap.get(value) || 0;
37582 optionsMap.put(value, count + 1);
37583 self.ngModelCtrl.$render();
37584 chromeHack(element);
37587 // Tell the select control that an option, with the given value, has been removed
37588 self.removeOption = function(value) {
37589 var count = optionsMap.get(value);
37592 optionsMap.remove(value);
37593 if (value === '') {
37594 self.emptyOption = undefined;
37597 optionsMap.put(value, count - 1);
37602 // Check whether the select control has an option matching the given value
37603 self.hasOption = function(value) {
37604 return !!optionsMap.get(value);
37608 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
37610 if (interpolateValueFn) {
37611 // The value attribute is interpolated
37613 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
37614 if (isDefined(oldVal)) {
37615 self.removeOption(oldVal);
37618 self.addOption(newVal, optionElement);
37620 } else if (interpolateTextFn) {
37621 // The text content is interpolated
37622 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
37623 optionAttrs.$set('value', newVal);
37624 if (oldVal !== newVal) {
37625 self.removeOption(oldVal);
37627 self.addOption(newVal, optionElement);
37630 // The value attribute is static
37631 self.addOption(optionAttrs.value, optionElement);
37634 optionElement.on('$destroy', function() {
37635 self.removeOption(optionAttrs.value);
37636 self.ngModelCtrl.$render();
37647 * HTML `SELECT` element with angular data-binding.
37649 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
37650 * between the scope and the `<select>` control (including setting default values).
37651 * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
37652 * {@link ngOptions `ngOptions`} directives.
37654 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
37655 * to the model identified by the `ngModel` directive. With static or repeated options, this is
37656 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
37657 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
37659 * <div class="alert alert-warning">
37660 * Note that the value of a `select` directive used without `ngOptions` is always a string.
37661 * When the model needs to be bound to a non-string value, you must either explictly convert it
37662 * using a directive (see example below) or use `ngOptions` to specify the set of options.
37663 * This is because an option element can only be bound to string values at present.
37666 * If the viewValue of `ngModel` does not match any of the options, then the control
37667 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
37669 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
37670 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
37671 * option. See example below for demonstration.
37673 * <div class="alert alert-info">
37674 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
37675 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
37676 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
37677 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
37678 * a new scope for each repeated instance.
37682 * @param {string} ngModel Assignable angular expression to data-bind to.
37683 * @param {string=} name Property name of the form under which the control is published.
37684 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
37685 * bound to the model as an array.
37686 * @param {string=} required Sets `required` validation error key if the value is not entered.
37687 * @param {string=} ngRequired Adds required attribute and required validation constraint to
37688 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
37689 * when you want to data-bind to the required attribute.
37690 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
37691 * interaction with the select element.
37692 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
37693 * set on the model on selection. See {@link ngOptions `ngOptions`}.
37696 * ### Simple `select` elements with static options
37698 * <example name="static-select" module="staticSelect">
37699 * <file name="index.html">
37700 * <div ng-controller="ExampleController">
37701 * <form name="myForm">
37702 * <label for="singleSelect"> Single select: </label><br>
37703 * <select name="singleSelect" ng-model="data.singleSelect">
37704 * <option value="option-1">Option 1</option>
37705 * <option value="option-2">Option 2</option>
37708 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
37709 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
37710 * <option value="">---Please select---</option> <!-- not selected / blank option -->
37711 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
37712 * <option value="option-2">Option 2</option>
37714 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
37715 * <tt>singleSelect = {{data.singleSelect}}</tt>
37718 * <label for="multipleSelect"> Multiple select: </label><br>
37719 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
37720 * <option value="option-1">Option 1</option>
37721 * <option value="option-2">Option 2</option>
37722 * <option value="option-3">Option 3</option>
37724 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
37728 * <file name="app.js">
37729 * angular.module('staticSelect', [])
37730 * .controller('ExampleController', ['$scope', function($scope) {
37732 * singleSelect: null,
37733 * multipleSelect: [],
37734 * option1: 'option-1',
37737 * $scope.forceUnknownOption = function() {
37738 * $scope.data.singleSelect = 'nonsense';
37744 * ### Using `ngRepeat` to generate `select` options
37745 * <example name="ngrepeat-select" module="ngrepeatSelect">
37746 * <file name="index.html">
37747 * <div ng-controller="ExampleController">
37748 * <form name="myForm">
37749 * <label for="repeatSelect"> Repeat select: </label>
37750 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
37751 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
37755 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
37758 * <file name="app.js">
37759 * angular.module('ngrepeatSelect', [])
37760 * .controller('ExampleController', ['$scope', function($scope) {
37762 * repeatSelect: null,
37763 * availableOptions: [
37764 * {id: '1', name: 'Option A'},
37765 * {id: '2', name: 'Option B'},
37766 * {id: '3', name: 'Option C'}
37774 * ### Using `select` with `ngOptions` and setting a default value
37775 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
37777 * <example name="select-with-default-values" module="defaultValueSelect">
37778 * <file name="index.html">
37779 * <div ng-controller="ExampleController">
37780 * <form name="myForm">
37781 * <label for="mySelect">Make a choice:</label>
37782 * <select name="mySelect" id="mySelect"
37783 * ng-options="option.name for option in data.availableOptions track by option.id"
37784 * ng-model="data.selectedOption"></select>
37787 * <tt>option = {{data.selectedOption}}</tt><br/>
37790 * <file name="app.js">
37791 * angular.module('defaultValueSelect', [])
37792 * .controller('ExampleController', ['$scope', function($scope) {
37794 * availableOptions: [
37795 * {id: '1', name: 'Option A'},
37796 * {id: '2', name: 'Option B'},
37797 * {id: '3', name: 'Option C'}
37799 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
37806 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
37808 * <example name="select-with-non-string-options" module="nonStringSelect">
37809 * <file name="index.html">
37810 * <select ng-model="model.id" convert-to-number>
37811 * <option value="0">Zero</option>
37812 * <option value="1">One</option>
37813 * <option value="2">Two</option>
37817 * <file name="app.js">
37818 * angular.module('nonStringSelect', [])
37819 * .run(function($rootScope) {
37820 * $rootScope.model = { id: 2 };
37822 * .directive('convertToNumber', function() {
37824 * require: 'ngModel',
37825 * link: function(scope, element, attrs, ngModel) {
37826 * ngModel.$parsers.push(function(val) {
37827 * return parseInt(val, 10);
37829 * ngModel.$formatters.push(function(val) {
37836 * <file name="protractor.js" type="protractor">
37837 * it('should initialize to model', function() {
37838 * var select = element(by.css('select'));
37839 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
37845 var selectDirective = function() {
37849 require: ['select', '?ngModel'],
37850 controller: SelectController,
37857 function selectPreLink(scope, element, attr, ctrls) {
37859 // if ngModel is not defined, we don't need to do anything
37860 var ngModelCtrl = ctrls[1];
37861 if (!ngModelCtrl) return;
37863 var selectCtrl = ctrls[0];
37865 selectCtrl.ngModelCtrl = ngModelCtrl;
37867 // We delegate rendering to the `writeValue` method, which can be changed
37868 // if the select can have multiple selected values or if the options are being
37869 // generated by `ngOptions`
37870 ngModelCtrl.$render = function() {
37871 selectCtrl.writeValue(ngModelCtrl.$viewValue);
37874 // When the selected item(s) changes we delegate getting the value of the select control
37875 // to the `readValue` method, which can be changed if the select can have multiple
37876 // selected values or if the options are being generated by `ngOptions`
37877 element.on('change', function() {
37878 scope.$apply(function() {
37879 ngModelCtrl.$setViewValue(selectCtrl.readValue());
37883 // If the select allows multiple values then we need to modify how we read and write
37884 // values from and to the control; also what it means for the value to be empty and
37885 // we have to add an extra watch since ngModel doesn't work well with arrays - it
37886 // doesn't trigger rendering if only an item in the array changes.
37887 if (attr.multiple) {
37889 // Read value now needs to check each option to see if it is selected
37890 selectCtrl.readValue = function readMultipleValue() {
37892 forEach(element.find('option'), function(option) {
37893 if (option.selected) {
37894 array.push(option.value);
37900 // Write value now needs to set the selected property of each matching option
37901 selectCtrl.writeValue = function writeMultipleValue(value) {
37902 var items = new HashMap(value);
37903 forEach(element.find('option'), function(option) {
37904 option.selected = isDefined(items.get(option.value));
37908 // we have to do it on each watch since ngModel watches reference, but
37909 // we need to work of an array, so we need to see if anything was inserted/removed
37910 var lastView, lastViewRef = NaN;
37911 scope.$watch(function selectMultipleWatch() {
37912 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
37913 lastView = shallowCopy(ngModelCtrl.$viewValue);
37914 ngModelCtrl.$render();
37916 lastViewRef = ngModelCtrl.$viewValue;
37919 // If we are a multiple select then value is now a collection
37920 // so the meaning of $isEmpty changes
37921 ngModelCtrl.$isEmpty = function(value) {
37922 return !value || value.length === 0;
37930 // The option directive is purely designed to communicate the existence (or lack of)
37931 // of dynamically created (and destroyed) option elements to their containing select
37932 // directive via its controller.
37933 var optionDirective = ['$interpolate', function($interpolate) {
37937 compile: function(element, attr) {
37939 if (isDefined(attr.value)) {
37940 // If the value attribute is defined, check if it contains an interpolation
37941 var interpolateValueFn = $interpolate(attr.value, true);
37943 // If the value attribute is not defined then we fall back to the
37944 // text content of the option element, which may be interpolated
37945 var interpolateTextFn = $interpolate(element.text(), true);
37946 if (!interpolateTextFn) {
37947 attr.$set('value', element.text());
37951 return function(scope, element, attr) {
37953 // This is an optimization over using ^^ since we don't want to have to search
37954 // all the way to the root of the DOM for every single option element
37955 var selectCtrlName = '$selectController',
37956 parent = element.parent(),
37957 selectCtrl = parent.data(selectCtrlName) ||
37958 parent.parent().data(selectCtrlName); // in case we are in optgroup
37961 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
37968 var styleDirective = valueFn({
37973 var requiredDirective = function() {
37976 require: '?ngModel',
37977 link: function(scope, elm, attr, ctrl) {
37979 attr.required = true; // force truthy in case we are on non input element
37981 ctrl.$validators.required = function(modelValue, viewValue) {
37982 return !attr.required || !ctrl.$isEmpty(viewValue);
37985 attr.$observe('required', function() {
37993 var patternDirective = function() {
37996 require: '?ngModel',
37997 link: function(scope, elm, attr, ctrl) {
38000 var regexp, patternExp = attr.ngPattern || attr.pattern;
38001 attr.$observe('pattern', function(regex) {
38002 if (isString(regex) && regex.length > 0) {
38003 regex = new RegExp('^' + regex + '$');
38006 if (regex && !regex.test) {
38007 throw minErr('ngPattern')('noregexp',
38008 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
38009 regex, startingTag(elm));
38012 regexp = regex || undefined;
38016 ctrl.$validators.pattern = function(modelValue, viewValue) {
38017 // HTML5 pattern constraint validates the input value, so we validate the viewValue
38018 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
38025 var maxlengthDirective = function() {
38028 require: '?ngModel',
38029 link: function(scope, elm, attr, ctrl) {
38032 var maxlength = -1;
38033 attr.$observe('maxlength', function(value) {
38034 var intVal = toInt(value);
38035 maxlength = isNaN(intVal) ? -1 : intVal;
38038 ctrl.$validators.maxlength = function(modelValue, viewValue) {
38039 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
38045 var minlengthDirective = function() {
38048 require: '?ngModel',
38049 link: function(scope, elm, attr, ctrl) {
38053 attr.$observe('minlength', function(value) {
38054 minlength = toInt(value) || 0;
38057 ctrl.$validators.minlength = function(modelValue, viewValue) {
38058 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
38064 if (window.angular.bootstrap) {
38065 //AngularJS is already loaded, so we can return here...
38066 console.log('WARNING: Tried to load angular more than once.');
38070 //try to bind to jquery now so that one can write jqLite(document).ready()
38071 //but we will rebind on bootstrap again.
38074 publishExternalAPI(angular);
38076 angular.module("ngLocale", [], ["$provide", function($provide) {
38077 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
38078 function getDecimals(n) {
38080 var i = n.indexOf('.');
38081 return (i == -1) ? 0 : n.length - i - 1;
38084 function getVF(n, opt_precision) {
38085 var v = opt_precision;
38087 if (undefined === v) {
38088 v = Math.min(getDecimals(n), 3);
38091 var base = Math.pow(10, v);
38092 var f = ((n * base) | 0) % base;
38093 return {v: v, f: f};
38096 $provide.value("$locale", {
38097 "DATETIME_FORMATS": {
38119 "FIRSTDAYOFWEEK": 6,
38161 "fullDate": "EEEE, MMMM d, y",
38162 "longDate": "MMMM d, y",
38163 "medium": "MMM d, y h:mm:ss a",
38164 "mediumDate": "MMM d, y",
38165 "mediumTime": "h:mm:ss a",
38166 "short": "M/d/yy h:mm a",
38167 "shortDate": "M/d/yy",
38168 "shortTime": "h:mm a"
38170 "NUMBER_FORMATS": {
38171 "CURRENCY_SYM": "$",
38172 "DECIMAL_SEP": ".",
38192 "negPre": "-\u00a4",
38194 "posPre": "\u00a4",
38200 "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;}
38205 * Setup file for the Scenario.
38206 * Must be first in the compilation/bootstrap list.
38209 // Public namespace
38210 angular.scenario = angular.scenario || {};
38213 * Expose jQuery (e.g. for custom dsl extensions).
38215 angular.scenario.jQuery = _jQuery;
38218 * Defines a new output format.
38220 * @param {string} name the name of the new output format
38221 * @param {function()} fn function(context, runner) that generates the output
38223 angular.scenario.output = angular.scenario.output || function(name, fn) {
38224 angular.scenario.output[name] = fn;
38228 * Defines a new DSL statement. If your factory function returns a Future
38229 * it's returned, otherwise the result is assumed to be a map of functions
38230 * for chaining. Chained functions are subject to the same rules.
38232 * Note: All functions on the chain are bound to the chain scope so values
38233 * set on "this" in your statement function are available in the chained
38236 * @param {string} name The name of the statement
38237 * @param {function()} fn Factory function(), return a function for
38240 angular.scenario.dsl = angular.scenario.dsl || function(name, fn) {
38241 angular.scenario.dsl[name] = function() {
38242 /* jshint -W040 *//* The dsl binds `this` for us when calling chained functions */
38243 function executeStatement(statement, args) {
38244 var result = statement.apply(this, args);
38245 if (angular.isFunction(result) || result instanceof angular.scenario.Future) {
38249 var chain = angular.extend({}, result);
38250 angular.forEach(chain, function(value, name) {
38251 if (angular.isFunction(value)) {
38252 chain[name] = function() {
38253 return executeStatement.call(self, value, arguments);
38256 chain[name] = value;
38261 var statement = fn.apply(this, arguments);
38262 return function() {
38263 return executeStatement.call(this, statement, arguments);
38269 * Defines a new matcher for use with the expects() statement. The value
38270 * this.actual (like in Jasmine) is available in your matcher to compare
38271 * against. Your function should return a boolean. The future is automatically
38274 * @param {string} name The name of the matcher
38275 * @param {function()} fn The matching function(expected).
38277 angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
38278 angular.scenario.matcher[name] = function(expected) {
38279 var description = this.future.name +
38280 (this.inverse ? ' not ' : ' ') + name +
38281 ' ' + angular.toJson(expected);
38283 this.addFuture('expect ' + description,
38286 self.actual = self.future.value;
38287 if ((self.inverse && fn.call(self, expected)) ||
38288 (!self.inverse && !fn.call(self, expected))) {
38289 error = 'expected ' + description +
38290 ' but was ' + angular.toJson(self.actual);
38298 * Initialize the scenario runner and run !
38300 * Access global window and document object
38301 * Access $runner through closure
38303 * @param {Object=} config Config options
38305 angular.scenario.setUpAndRun = function(config) {
38306 var href = window.location.href;
38307 var body = _jQuery(document.body);
38309 var objModel = new angular.scenario.ObjectModel($runner);
38311 if (config && config.scenario_output) {
38312 output = config.scenario_output.split(',');
38315 angular.forEach(angular.scenario.output, function(fn, name) {
38316 if (!output.length || output.indexOf(name) != -1) {
38317 var context = body.append('<div></div>').find('div:last');
38318 context.attr('id', name);
38319 fn.call({}, context, $runner, objModel);
38323 if (!/^http/.test(href) && !/^https/.test(href)) {
38324 body.append('<p id="system-error"></p>');
38325 body.find('#system-error').text(
38326 'Scenario runner must be run using http or https. The protocol ' +
38327 href.split(':')[0] + ':// is not supported.'
38332 var appFrame = body.append('<div id="application"></div>').find('#application');
38333 var application = new angular.scenario.Application(appFrame);
38335 $runner.on('RunnerEnd', function() {
38336 appFrame.css('display', 'none');
38337 appFrame.find('iframe').attr('src', 'about:blank');
38340 $runner.on('RunnerError', function(error) {
38341 if (window.console) {
38342 console.log(formatException(error));
38344 // Do something for IE
38349 $runner.run(application);
38353 * Iterates through list with iterator function that must call the
38354 * continueFunction to continue iterating.
38356 * @param {Array} list list to iterate over
38357 * @param {function()} iterator Callback function(value, continueFunction)
38358 * @param {function()} done Callback function(error, result) called when
38359 * iteration finishes or an error occurs.
38361 function asyncForEach(list, iterator, done) {
38363 function loop(error, index) {
38364 if (index && index > i) {
38367 if (error || i >= list.length) {
38371 iterator(list[i++], loop);
38381 * Formats an exception into a string with the stack trace, but limits
38382 * to a specific line length.
38384 * @param {Object} error The exception to format, can be anything throwable
38385 * @param {Number=} [maxStackLines=5] max lines of the stack trace to include
38388 function formatException(error, maxStackLines) {
38389 maxStackLines = maxStackLines || 5;
38390 var message = error.toString();
38392 var stack = error.stack.split('\n');
38393 if (stack[0].indexOf(message) === -1) {
38395 stack.unshift(error.message);
38397 message = stack.slice(0, maxStackLines).join('\n');
38403 * Returns a function that gets the file name and line number from a
38404 * location in the stack if available based on the call site.
38406 * Note: this returns another function because accessing .stack is very
38407 * expensive in Chrome.
38409 * @param {Number} offset Number of stack lines to skip
38411 function callerFile(offset) {
38412 var error = new Error();
38414 return function() {
38415 var line = (error.stack || '').split('\n')[offset];
38417 // Clean up the stack trace line
38419 if (line.indexOf('@') !== -1) {
38421 line = line.substring(line.indexOf('@') + 1);
38424 line = line.substring(line.indexOf('(') + 1).replace(')', '');
38434 * Don't use the jQuery trigger method since it works incorrectly.
38436 * jQuery notifies listeners and then changes the state of a checkbox and
38437 * does not create a real browser event. A real click changes the state of
38438 * the checkbox and then notifies listeners.
38440 * To work around this we instead use our own handler that fires a real event.
38443 // We need a handle to the original trigger function for input tests.
38444 var parentTrigger = fn._originalTrigger = fn.trigger;
38445 fn.trigger = function(type) {
38446 if (/(click|change|keydown|blur|input|mousedown|mouseup)/.test(type)) {
38447 var processDefaults = [];
38448 this.each(function(index, node) {
38449 processDefaults.push(browserTrigger(node, type));
38452 // this is not compatible with jQuery - we return an array of returned values,
38453 // so that scenario runner know whether JS code has preventDefault() of the event or not...
38454 return processDefaults;
38456 return parentTrigger.apply(this, arguments);
38461 * Finds all bindings with the substring match of name and returns an
38462 * array of their values.
38464 * @param {string} bindExp The name to match
38465 * @return {Array.<string>} String of binding values
38467 _jQuery.fn.bindings = function(windowJquery, bindExp) {
38468 var result = [], match,
38469 bindSelector = '.ng-binding:visible';
38470 if (angular.isString(bindExp)) {
38471 bindExp = bindExp.replace(/\s/g, '');
38472 match = function(actualExp) {
38474 actualExp = actualExp.replace(/\s/g, '');
38475 if (actualExp == bindExp) return true;
38476 if (actualExp.indexOf(bindExp) === 0) {
38477 return actualExp.charAt(bindExp.length) == '|';
38481 } else if (bindExp) {
38482 match = function(actualExp) {
38483 return actualExp && bindExp.exec(actualExp);
38486 match = function(actualExp) {
38487 return !!actualExp;
38490 var selection = this.find(bindSelector);
38491 if (this.is(bindSelector)) {
38492 selection = selection.add(this);
38495 function push(value) {
38496 if (angular.isUndefined(value)) {
38498 } else if (typeof value !== 'string') {
38499 value = angular.toJson(value);
38501 result.push('' + value);
38504 selection.each(function() {
38505 var element = windowJquery(this),
38507 if (bindings = element.data('$binding')) {
38508 for (var expressions = [], binding, j=0, jj=bindings.length; j < jj; j++) {
38509 binding = bindings[j];
38511 if (binding.expressions) {
38512 expressions = binding.expressions;
38514 expressions = [binding];
38516 for (var scope, expression, i = 0, ii = expressions.length; i < ii; i++) {
38517 expression = expressions[i];
38518 if (match(expression)) {
38519 scope = scope || element.scope();
38520 push(scope.$eval(expression));
38531 * Triggers a browser event. Attempts to choose the right event if one is
38534 * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
38535 * @param {string} eventType Optional event type
38536 * @param {Object=} eventData An optional object which contains additional event data (such as x,y
38537 * coordinates, keys, etc...) that are passed into the event when triggered
38539 window.browserTrigger = function browserTrigger(element, eventType, eventData) {
38540 if (element && !element.nodeName) element = element[0];
38541 if (!element) return;
38543 eventData = eventData || {};
38544 var relatedTarget = eventData.relatedTarget || element;
38545 var keys = eventData.keys;
38546 var x = eventData.x;
38547 var y = eventData.y;
38549 var inputType = (element.type) ? element.type.toLowerCase() : null,
38550 nodeName = element.nodeName.toLowerCase();
38554 'textarea': 'change',
38555 'hidden': 'change',
38556 'password': 'change',
38561 'checkbox': 'click',
38563 'select-one': 'change',
38564 'select-multiple': 'change',
38565 '_default_': 'click'
38566 }[inputType || '_default_'];
38569 if (nodeName == 'option') {
38570 element.parentNode.value = element.value;
38571 element = element.parentNode;
38572 eventType = 'change';
38576 function pressed(key) {
38577 return keys.indexOf(key) !== -1;
38581 if (/transitionend/.test(eventType)) {
38582 if (window.WebKitTransitionEvent) {
38583 evnt = new WebKitTransitionEvent(eventType, eventData);
38584 evnt.initEvent(eventType, false, true);
38587 evnt = new TransitionEvent(eventType, eventData);
38590 evnt = document.createEvent('TransitionEvent');
38591 evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
38594 } else if (/animationend/.test(eventType)) {
38595 if (window.WebKitAnimationEvent) {
38596 evnt = new WebKitAnimationEvent(eventType, eventData);
38597 evnt.initEvent(eventType, false, true);
38600 evnt = new AnimationEvent(eventType, eventData);
38603 evnt = document.createEvent('AnimationEvent');
38604 evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
38607 } else if (/touch/.test(eventType) && supportsTouchEvents()) {
38608 evnt = createTouchEvent(element, eventType, x, y);
38610 evnt = document.createEvent('MouseEvents');
38613 evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
38614 pressed('alt'), pressed('shift'), pressed('meta'), 0, relatedTarget);
38617 /* we're unable to change the timeStamp value directly so this
38618 * is only here to allow for testing where the timeStamp value is
38620 evnt.$manualTimeStamp = eventData.timeStamp;
38624 var originalPreventDefault = evnt.preventDefault,
38625 appWindow = element.ownerDocument.defaultView,
38626 fakeProcessDefault = true,
38627 finalProcessDefault,
38628 angular = appWindow.angular || {};
38630 // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
38631 angular['ff-684208-preventDefault'] = false;
38632 evnt.preventDefault = function() {
38633 fakeProcessDefault = false;
38634 return originalPreventDefault.apply(evnt, arguments);
38637 element.dispatchEvent(evnt);
38638 finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
38640 delete angular['ff-684208-preventDefault'];
38642 return finalProcessDefault;
38645 function supportsTouchEvents() {
38646 if ('_cached' in supportsTouchEvents) {
38647 return supportsTouchEvents._cached;
38649 if (!document.createTouch || !document.createTouchList) {
38650 supportsTouchEvents._cached = false;
38654 document.createEvent('TouchEvent');
38656 supportsTouchEvents._cached = false;
38659 supportsTouchEvents._cached = true;
38663 function createTouchEvent(element, eventType, x, y) {
38664 var evnt = new Event(eventType);
38668 var touch = document.createTouch(window, element, Date.now(), x, y, x, y);
38669 var touches = document.createTouchList(touch);
38671 evnt.touches = touches;
38678 * Represents the application currently being tested and abstracts usage
38679 * of iframes or separate windows.
38681 * @param {Object} context jQuery wrapper around HTML context.
38683 angular.scenario.Application = function(context) {
38684 this.context = context;
38686 '<h2>Current URL: <a href="about:blank">None</a></h2>' +
38687 '<div id="test-frames"></div>'
38692 * Gets the jQuery collection of frames. Don't use this directly because
38693 * frames may go stale.
38696 * @return {Object} jQuery collection
38698 angular.scenario.Application.prototype.getFrame_ = function() {
38699 return this.context.find('#test-frames iframe:last');
38703 * Gets the window of the test runner frame. Always favor executeAction()
38704 * instead of this method since it prevents you from getting a stale window.
38707 * @return {Object} the window of the frame
38709 angular.scenario.Application.prototype.getWindow_ = function() {
38710 var contentWindow = this.getFrame_().prop('contentWindow');
38711 if (!contentWindow) {
38712 throw 'Frame window is not accessible.';
38714 return contentWindow;
38718 * Changes the location of the frame.
38720 * @param {string} url The URL. If it begins with a # then only the
38721 * hash of the page is changed.
38722 * @param {function()} loadFn function($window, $document) Called when frame loads.
38723 * @param {function()} errorFn function(error) Called if any error when loading.
38725 angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorFn) {
38727 var frame = self.getFrame_();
38728 //TODO(esprehn): Refactor to use rethrow()
38729 errorFn = errorFn || function(e) { throw e; };
38730 if (url === 'about:blank') {
38731 errorFn('Sandbox Error: Navigating to about:blank is not allowed.');
38732 } else if (url.charAt(0) === '#') {
38733 url = frame.attr('src').split('#')[0] + url;
38734 frame.attr('src', url);
38735 self.executeAction(loadFn);
38738 self.context.find('#test-frames').append('<iframe>');
38739 frame = self.getFrame_();
38741 frame.load(function() {
38744 var $window = self.getWindow_();
38746 if (!$window.angular) {
38747 self.executeAction(loadFn);
38751 if (!$window.angular.resumeBootstrap) {
38752 $window.angular.resumeDeferredBootstrap = resumeDeferredBootstrap;
38754 resumeDeferredBootstrap();
38761 function resumeDeferredBootstrap() {
38762 // Disable animations
38763 var $injector = $window.angular.resumeBootstrap([['$provide', function($provide) {
38764 return ['$animate', function($animate) {
38765 $animate.enabled(false);
38768 self.rootElement = $injector.get('$rootElement')[0];
38769 self.executeAction(loadFn);
38771 }).attr('src', url);
38773 // for IE compatibility set the name *after* setting the frame url
38774 frame[0].contentWindow.name = "NG_DEFER_BOOTSTRAP!";
38776 self.context.find('> h2 a').attr('href', url).text(url);
38780 * Executes a function in the context of the tested application. Will wait
38781 * for all pending angular xhr requests before executing.
38783 * @param {function()} action The callback to execute. function($window, $document)
38784 * $document is a jQuery wrapped document.
38786 angular.scenario.Application.prototype.executeAction = function(action) {
38788 var $window = this.getWindow_();
38789 if (!$window.document) {
38790 throw 'Sandbox Error: Application document not accessible.';
38792 if (!$window.angular) {
38793 return action.call(this, $window, _jQuery($window.document));
38796 if (!!this.rootElement) {
38797 executeWithElement(this.rootElement);
38799 angularInit($window.document, angular.bind(this, executeWithElement));
38802 function executeWithElement(element) {
38803 var $injector = $window.angular.element(element).injector();
38804 var $element = _jQuery(element);
38806 $element.injector = function() {
38810 $injector.invoke(function($browser) {
38811 $browser.notifyWhenNoOutstandingRequests(function() {
38812 action.call(self, $window, $element);
38819 * The representation of define blocks. Don't used directly, instead use
38820 * define() in your tests.
38822 * @param {string} descName Name of the block
38823 * @param {Object} parent describe or undefined if the root.
38825 angular.scenario.Describe = function(descName, parent) {
38826 this.only = parent && parent.only;
38827 this.beforeEachFns = [];
38828 this.afterEachFns = [];
38830 this.children = [];
38831 this.name = descName;
38832 this.parent = parent;
38833 this.id = angular.scenario.Describe.id++;
38836 * Calls all before functions.
38838 var beforeEachFns = this.beforeEachFns;
38839 this.setupBefore = function() {
38840 if (parent) parent.setupBefore.call(this);
38841 angular.forEach(beforeEachFns, function(fn) { fn.call(this); }, this);
38845 * Calls all after functions.
38847 var afterEachFns = this.afterEachFns;
38848 this.setupAfter = function() {
38849 angular.forEach(afterEachFns, function(fn) { fn.call(this); }, this);
38850 if (parent) parent.setupAfter.call(this);
38854 // Shared Unique ID generator for every describe block
38855 angular.scenario.Describe.id = 0;
38857 // Shared Unique ID generator for every it (spec)
38858 angular.scenario.Describe.specId = 0;
38861 * Defines a block to execute before each it or nested describe.
38863 * @param {function()} body Body of the block.
38865 angular.scenario.Describe.prototype.beforeEach = function(body) {
38866 this.beforeEachFns.push(body);
38870 * Defines a block to execute after each it or nested describe.
38872 * @param {function()} body Body of the block.
38874 angular.scenario.Describe.prototype.afterEach = function(body) {
38875 this.afterEachFns.push(body);
38879 * Creates a new describe block that's a child of this one.
38881 * @param {string} name Name of the block. Appended to the parent block's name.
38882 * @param {function()} body Body of the block.
38884 angular.scenario.Describe.prototype.describe = function(name, body) {
38885 var child = new angular.scenario.Describe(name, this);
38886 this.children.push(child);
38891 * Same as describe() but makes ddescribe blocks the only to run.
38893 * @param {string} name Name of the test.
38894 * @param {function()} body Body of the block.
38896 angular.scenario.Describe.prototype.ddescribe = function(name, body) {
38897 var child = new angular.scenario.Describe(name, this);
38899 this.children.push(child);
38904 * Use to disable a describe block.
38906 angular.scenario.Describe.prototype.xdescribe = angular.noop;
38911 * @param {string} name Name of the test.
38912 * @param {function()} body Body of the block.
38914 angular.scenario.Describe.prototype.it = function(name, body) {
38916 id: angular.scenario.Describe.specId++,
38920 before: this.setupBefore,
38922 after: this.setupAfter
38927 * Same as it() but makes iit tests the only test to run.
38929 * @param {string} name Name of the test.
38930 * @param {function()} body Body of the block.
38932 angular.scenario.Describe.prototype.iit = function(name, body) {
38933 this.it.apply(this, arguments);
38934 this.its[this.its.length - 1].only = true;
38938 * Use to disable a test block.
38940 angular.scenario.Describe.prototype.xit = angular.noop;
38943 * Gets an array of functions representing all the tests (recursively).
38944 * that can be executed with SpecRunner's.
38946 * @return {Array<Object>} Array of it blocks {
38947 * definition : Object // parent Describe
38955 angular.scenario.Describe.prototype.getSpecs = function() {
38956 var specs = arguments[0] || [];
38957 angular.forEach(this.children, function(child) {
38958 child.getSpecs(specs);
38960 angular.forEach(this.its, function(it) {
38964 angular.forEach(specs, function(it) {
38969 return (only.length && only) || specs;
38973 * A future action in a spec.
38975 * @param {string} name name of the future action
38976 * @param {function()} behavior future callback(error, result)
38977 * @param {function()} line Optional. function that returns the file/line number.
38979 angular.scenario.Future = function(name, behavior, line) {
38981 this.behavior = behavior;
38982 this.fulfilled = false;
38983 this.value = undefined;
38984 this.parser = angular.identity;
38985 this.line = line || function() { return ''; };
38989 * Executes the behavior of the closure.
38991 * @param {function()} doneFn Callback function(error, result)
38993 angular.scenario.Future.prototype.execute = function(doneFn) {
38995 this.behavior(function(error, result) {
38996 self.fulfilled = true;
38999 result = self.parser(result);
39004 self.value = error || result;
39005 doneFn(error, result);
39010 * Configures the future to convert its final with a function fn(value)
39012 * @param {function()} fn function(value) that returns the parsed value
39014 angular.scenario.Future.prototype.parsedWith = function(fn) {
39020 * Configures the future to parse its final value from JSON
39023 angular.scenario.Future.prototype.fromJson = function() {
39024 return this.parsedWith(angular.fromJson);
39028 * Configures the future to convert its final value from objects
39031 angular.scenario.Future.prototype.toJson = function() {
39032 return this.parsedWith(angular.toJson);
39036 * Maintains an object tree from the runner events.
39038 * @param {Object} runner The scenario Runner instance to connect to.
39040 * TODO(esprehn): Every output type creates one of these, but we probably
39041 * want one global shared instance. Need to handle events better too
39042 * so the HTML output doesn't need to do spec model.getSpec(spec.id)
39045 * TODO(vojta) refactor on, emit methods (from all objects) - use inheritance
39047 angular.scenario.ObjectModel = function(runner) {
39051 this.listeners = [];
39057 runner.on('SpecBegin', function(spec) {
39058 var block = self.value,
39061 angular.forEach(self.getDefinitionPath(spec), function(def) {
39062 if (!block.children[def.name]) {
39063 block.children[def.name] = {
39070 block = block.children[def.name];
39071 definitions.push(def.name);
39074 var it = self.specMap[spec.id] =
39075 block.specs[spec.name] =
39076 new angular.scenario.ObjectModel.Spec(spec.id, spec.name, definitions);
39078 // forward the event
39079 self.emit('SpecBegin', it);
39082 runner.on('SpecError', function(spec, error) {
39083 var it = self.getSpec(spec.id);
39084 it.status = 'error';
39087 // forward the event
39088 self.emit('SpecError', it, error);
39091 runner.on('SpecEnd', function(spec) {
39092 var it = self.getSpec(spec.id);
39095 // forward the event
39096 self.emit('SpecEnd', it);
39099 runner.on('StepBegin', function(spec, step) {
39100 var it = self.getSpec(spec.id);
39101 step = new angular.scenario.ObjectModel.Step(step.name);
39102 it.steps.push(step);
39104 // forward the event
39105 self.emit('StepBegin', it, step);
39108 runner.on('StepEnd', function(spec) {
39109 var it = self.getSpec(spec.id);
39110 var step = it.getLastStep();
39111 if (step.name !== step.name) {
39112 throw 'Events fired in the wrong order. Step names don\'t match.';
39116 // forward the event
39117 self.emit('StepEnd', it, step);
39120 runner.on('StepFailure', function(spec, step, error) {
39121 var it = self.getSpec(spec.id),
39122 modelStep = it.getLastStep();
39124 modelStep.setErrorStatus('failure', error, step.line());
39125 it.setStatusFromStep(modelStep);
39127 // forward the event
39128 self.emit('StepFailure', it, modelStep, error);
39131 runner.on('StepError', function(spec, step, error) {
39132 var it = self.getSpec(spec.id),
39133 modelStep = it.getLastStep();
39135 modelStep.setErrorStatus('error', error, step.line());
39136 it.setStatusFromStep(modelStep);
39138 // forward the event
39139 self.emit('StepError', it, modelStep, error);
39142 runner.on('RunnerBegin', function() {
39143 self.emit('RunnerBegin');
39145 runner.on('RunnerEnd', function() {
39146 self.emit('RunnerEnd');
39149 function complete(item) {
39150 item.endTime = Date.now();
39151 item.duration = item.endTime - item.startTime;
39152 item.status = item.status || 'success';
39157 * Adds a listener for an event.
39159 * @param {string} eventName Name of the event to add a handler for
39160 * @param {function()} listener Function that will be called when event is fired
39162 angular.scenario.ObjectModel.prototype.on = function(eventName, listener) {
39163 eventName = eventName.toLowerCase();
39164 this.listeners[eventName] = this.listeners[eventName] || [];
39165 this.listeners[eventName].push(listener);
39169 * Emits an event which notifies listeners and passes extra
39172 * @param {string} eventName Name of the event to fire.
39174 angular.scenario.ObjectModel.prototype.emit = function(eventName) {
39176 args = Array.prototype.slice.call(arguments, 1);
39178 eventName = eventName.toLowerCase();
39180 if (this.listeners[eventName]) {
39181 angular.forEach(this.listeners[eventName], function(listener) {
39182 listener.apply(self, args);
39188 * Computes the path of definition describe blocks that wrap around
39191 * @param spec Spec to compute the path for.
39192 * @return {Array<Describe>} The describe block path
39194 angular.scenario.ObjectModel.prototype.getDefinitionPath = function(spec) {
39196 var currentDefinition = spec.definition;
39197 while (currentDefinition && currentDefinition.name) {
39198 path.unshift(currentDefinition);
39199 currentDefinition = currentDefinition.parent;
39205 * Gets a spec by id.
39207 * @param {string} id The id of the spec to get the object for.
39208 * @return {Object} the Spec instance
39210 angular.scenario.ObjectModel.prototype.getSpec = function(id) {
39211 return this.specMap[id];
39215 * A single it block.
39217 * @param {string} id Id of the spec
39218 * @param {string} name Name of the spec
39219 * @param {Array<string>=} definitionNames List of all describe block names that wrap this spec
39221 angular.scenario.ObjectModel.Spec = function(id, name, definitionNames) {
39224 this.startTime = Date.now();
39226 this.fullDefinitionName = (definitionNames || []).join(' ');
39230 * Adds a new step to the Spec.
39232 * @param {string} name Name of the step (really name of the future)
39233 * @return {Object} the added step
39235 angular.scenario.ObjectModel.Spec.prototype.addStep = function(name) {
39236 var step = new angular.scenario.ObjectModel.Step(name);
39237 this.steps.push(step);
39242 * Gets the most recent step.
39244 * @return {Object} the step
39246 angular.scenario.ObjectModel.Spec.prototype.getLastStep = function() {
39247 return this.steps[this.steps.length - 1];
39251 * Set status of the Spec from given Step
39253 * @param {angular.scenario.ObjectModel.Step} step
39255 angular.scenario.ObjectModel.Spec.prototype.setStatusFromStep = function(step) {
39256 if (!this.status || step.status == 'error') {
39257 this.status = step.status;
39258 this.error = step.error;
39259 this.line = step.line;
39264 * A single step inside a Spec.
39266 * @param {string} name Name of the step
39268 angular.scenario.ObjectModel.Step = function(name) {
39270 this.startTime = Date.now();
39274 * Helper method for setting all error status related properties
39276 * @param {string} status
39277 * @param {string} error
39278 * @param {string} line
39280 angular.scenario.ObjectModel.Step.prototype.setErrorStatus = function(status, error, line) {
39281 this.status = status;
39282 this.error = error;
39287 * Runner for scenarios
39289 * Has to be initialized before any test is loaded,
39290 * because it publishes the API into window (global space).
39292 angular.scenario.Runner = function($window) {
39293 this.listeners = [];
39294 this.$window = $window;
39295 this.rootDescribe = new angular.scenario.Describe();
39296 this.currentDescribe = this.rootDescribe;
39301 describe: this.describe,
39302 ddescribe: this.ddescribe,
39303 xdescribe: angular.noop,
39304 beforeEach: this.beforeEach,
39305 afterEach: this.afterEach
39307 angular.forEach(this.api, angular.bind(this, function(fn, key) {
39308 this.$window[key] = angular.bind(this, fn);
39313 * Emits an event which notifies listeners and passes extra
39316 * @param {string} eventName Name of the event to fire.
39318 angular.scenario.Runner.prototype.emit = function(eventName) {
39320 var args = Array.prototype.slice.call(arguments, 1);
39321 eventName = eventName.toLowerCase();
39322 if (!this.listeners[eventName]) {
39325 angular.forEach(this.listeners[eventName], function(listener) {
39326 listener.apply(self, args);
39331 * Adds a listener for an event.
39333 * @param {string} eventName The name of the event to add a handler for
39334 * @param {string} listener The fn(...) that takes the extra arguments from emit()
39336 angular.scenario.Runner.prototype.on = function(eventName, listener) {
39337 eventName = eventName.toLowerCase();
39338 this.listeners[eventName] = this.listeners[eventName] || [];
39339 this.listeners[eventName].push(listener);
39343 * Defines a describe block of a spec.
39347 * @param {string} name Name of the block
39348 * @param {function()} body Body of the block
39350 angular.scenario.Runner.prototype.describe = function(name, body) {
39352 this.currentDescribe.describe(name, function() {
39353 var parentDescribe = self.currentDescribe;
39354 self.currentDescribe = this;
39358 self.currentDescribe = parentDescribe;
39364 * Same as describe, but makes ddescribe the only blocks to run.
39368 * @param {string} name Name of the block
39369 * @param {function()} body Body of the block
39371 angular.scenario.Runner.prototype.ddescribe = function(name, body) {
39373 this.currentDescribe.ddescribe(name, function() {
39374 var parentDescribe = self.currentDescribe;
39375 self.currentDescribe = this;
39379 self.currentDescribe = parentDescribe;
39385 * Defines a test in a describe block of a spec.
39389 * @param {string} name Name of the block
39390 * @param {function()} body Body of the block
39392 angular.scenario.Runner.prototype.it = function(name, body) {
39393 this.currentDescribe.it(name, body);
39397 * Same as it, but makes iit tests the only tests to run.
39401 * @param {string} name Name of the block
39402 * @param {function()} body Body of the block
39404 angular.scenario.Runner.prototype.iit = function(name, body) {
39405 this.currentDescribe.iit(name, body);
39409 * Defines a function to be called before each it block in the describe
39410 * (and before all nested describes).
39414 * @param {function()} Callback to execute
39416 angular.scenario.Runner.prototype.beforeEach = function(body) {
39417 this.currentDescribe.beforeEach(body);
39421 * Defines a function to be called after each it block in the describe
39422 * (and before all nested describes).
39426 * @param {function()} Callback to execute
39428 angular.scenario.Runner.prototype.afterEach = function(body) {
39429 this.currentDescribe.afterEach(body);
39433 * Creates a new spec runner.
39436 * @param {Object} scope parent scope
39438 angular.scenario.Runner.prototype.createSpecRunner_ = function(scope) {
39439 var child = scope.$new();
39440 var Cls = angular.scenario.SpecRunner;
39442 // Export all the methods to child scope manually as now we don't mess controllers with scopes
39443 // TODO(vojta): refactor scenario runner so that these objects are not tightly coupled as current
39444 for (var name in Cls.prototype) {
39445 child[name] = angular.bind(child, Cls.prototype[name]);
39453 * Runs all the loaded tests with the specified runner class on the
39454 * provided application.
39456 * @param {angular.scenario.Application} application App to remote control.
39458 angular.scenario.Runner.prototype.run = function(application) {
39460 var $root = angular.injector(['ng']).get('$rootScope');
39461 angular.extend($root, this);
39462 angular.forEach(angular.scenario.Runner.prototype, function(fn, name) {
39463 $root[name] = angular.bind(self, fn);
39465 $root.application = application;
39466 $root.emit('RunnerBegin');
39467 asyncForEach(this.rootDescribe.getSpecs(), function(spec, specDone) {
39469 var runner = self.createSpecRunner_($root);
39470 angular.forEach(angular.scenario.dsl, function(fn, key) {
39471 dslCache[key] = fn.call($root);
39473 angular.forEach(angular.scenario.dsl, function(fn, key) {
39474 self.$window[key] = function() {
39475 var line = callerFile(3);
39476 var scope = runner.$new();
39478 // Make the dsl accessible on the current chain
39480 angular.forEach(dslCache, function(fn, key) {
39481 scope.dsl[key] = function() {
39482 return dslCache[key].apply(scope, arguments);
39486 // Make these methods work on the current chain
39487 scope.addFuture = function() {
39488 Array.prototype.push.call(arguments, line);
39489 return angular.scenario.SpecRunner.
39490 prototype.addFuture.apply(scope, arguments);
39492 scope.addFutureAction = function() {
39493 Array.prototype.push.call(arguments, line);
39494 return angular.scenario.SpecRunner.
39495 prototype.addFutureAction.apply(scope, arguments);
39498 return scope.dsl[key].apply(scope, arguments);
39501 runner.run(spec, function() {
39503 specDone.apply(this, arguments);
39508 self.emit('RunnerError', error);
39510 self.emit('RunnerEnd');
39515 * This class is the "this" of the it/beforeEach/afterEach method.
39516 * Responsibilities:
39517 * - "this" for it/beforeEach/afterEach
39518 * - keep state for single it/beforeEach/afterEach execution
39519 * - keep track of all of the futures to execute
39520 * - run single spec (execute each future)
39522 angular.scenario.SpecRunner = function() {
39524 this.afterIndex = 0;
39528 * Executes a spec which is an it block with associated before/after functions
39529 * based on the describe nesting.
39531 * @param {Object} spec A spec object
39532 * @param {function()} specDone function that is called when the spec finishes,
39533 * of the form `Function(error, index)`
39535 angular.scenario.SpecRunner.prototype.run = function(spec, specDone) {
39539 this.emit('SpecBegin', spec);
39542 spec.before.call(this);
39543 spec.body.call(this);
39544 this.afterIndex = this.futures.length;
39545 spec.after.call(this);
39547 this.emit('SpecError', spec, e);
39548 this.emit('SpecEnd', spec);
39553 var handleError = function(error, done) {
39558 done(null, self.afterIndex);
39563 function(future, futureDone) {
39564 self.step = future;
39565 self.emit('StepBegin', spec, future);
39567 future.execute(function(error) {
39569 self.emit('StepFailure', spec, future, error);
39570 self.emit('StepEnd', spec, future);
39571 return handleError(error, futureDone);
39573 self.emit('StepEnd', spec, future);
39574 self.$window.setTimeout(function() { futureDone(); }, 0);
39577 self.emit('StepError', spec, future, e);
39578 self.emit('StepEnd', spec, future);
39579 handleError(e, futureDone);
39584 self.emit('SpecError', spec, e);
39586 self.emit('SpecEnd', spec);
39587 // Call done in a timeout so exceptions don't recursively
39588 // call this function
39589 self.$window.setTimeout(function() { specDone(); }, 0);
39595 * Adds a new future action.
39597 * Note: Do not pass line manually. It happens automatically.
39599 * @param {string} name Name of the future
39600 * @param {function()} behavior Behavior of the future
39601 * @param {function()} line fn() that returns file/line number
39603 angular.scenario.SpecRunner.prototype.addFuture = function(name, behavior, line) {
39604 var future = new angular.scenario.Future(name, angular.bind(this, behavior), line);
39605 this.futures.push(future);
39610 * Adds a new future action to be executed on the application window.
39612 * Note: Do not pass line manually. It happens automatically.
39614 * @param {string} name Name of the future
39615 * @param {function()} behavior Behavior of the future
39616 * @param {function()} line fn() that returns file/line number
39618 angular.scenario.SpecRunner.prototype.addFutureAction = function(name, behavior, line) {
39620 var NG = /\[ng\\\:/;
39621 return this.addFuture(name, function(done) {
39622 this.application.executeAction(function($window, $document) {
39624 //TODO(esprehn): Refactor this so it doesn't need to be in here.
39625 $document.elements = function(selector) {
39626 var args = Array.prototype.slice.call(arguments, 1);
39627 selector = (self.selector || '') + ' ' + (selector || '');
39628 selector = _jQuery.trim(selector) || '*';
39629 angular.forEach(args, function(value, index) {
39630 selector = selector.replace('$' + (index + 1), value);
39632 var result = $document.find(selector);
39633 if (selector.match(NG)) {
39634 angular.forEach(['[ng-','[data-ng-','[x-ng-'], function(value, index) {
39635 result = result.add(selector.replace(NG, value), $document);
39638 if (!result.length) {
39641 message: 'Selector ' + selector + ' did not match any elements.'
39649 behavior.call(self, $window, $document, done);
39651 if (e.type && e.type === 'selector') {
39662 * Shared DSL statements that are useful to all scenarios.
39667 * pause() pauses until you call resume() in the console
39669 angular.scenario.dsl('pause', function() {
39670 return function() {
39671 return this.addFuture('pausing for you to resume', function(done) {
39672 this.emit('InteractivePause', this.spec, this.step);
39673 this.$window.resume = function() { done(); };
39680 * sleep(seconds) pauses the test for specified number of seconds
39682 angular.scenario.dsl('sleep', function() {
39683 return function(time) {
39684 return this.addFuture('sleep for ' + time + ' seconds', function(done) {
39685 this.$window.setTimeout(function() { done(null, time * 1000); }, time * 1000);
39692 * browser().navigateTo(url) Loads the url into the frame
39693 * browser().navigateTo(url, fn) where fn(url) is called and returns the URL to navigate to
39694 * browser().reload() refresh the page (reload the same URL)
39695 * browser().window.href() window.location.href
39696 * browser().window.path() window.location.pathname
39697 * browser().window.search() window.location.search
39698 * browser().window.hash() window.location.hash without # prefix
39699 * browser().location().url() see ng.$location#url
39700 * browser().location().path() see ng.$location#path
39701 * browser().location().search() see ng.$location#search
39702 * browser().location().hash() see ng.$location#hash
39704 angular.scenario.dsl('browser', function() {
39707 chain.navigateTo = function(url, delegate) {
39708 var application = this.application;
39709 return this.addFuture("browser navigate to '" + url + "'", function(done) {
39711 url = delegate.call(this, url);
39713 application.navigateTo(url, function() {
39719 chain.reload = function() {
39720 var application = this.application;
39721 return this.addFutureAction('browser reload', function($window, $document, done) {
39722 var href = $window.location.href;
39723 application.navigateTo(href, function() {
39729 chain.window = function() {
39732 api.href = function() {
39733 return this.addFutureAction('window.location.href', function($window, $document, done) {
39734 done(null, $window.location.href);
39738 api.path = function() {
39739 return this.addFutureAction('window.location.path', function($window, $document, done) {
39740 done(null, $window.location.pathname);
39744 api.search = function() {
39745 return this.addFutureAction('window.location.search', function($window, $document, done) {
39746 done(null, $window.location.search);
39750 api.hash = function() {
39751 return this.addFutureAction('window.location.hash', function($window, $document, done) {
39752 done(null, $window.location.hash.replace('#', ''));
39759 chain.location = function() {
39762 api.url = function() {
39763 return this.addFutureAction('$location.url()', function($window, $document, done) {
39764 done(null, $document.injector().get('$location').url());
39768 api.path = function() {
39769 return this.addFutureAction('$location.path()', function($window, $document, done) {
39770 done(null, $document.injector().get('$location').path());
39774 api.search = function() {
39775 return this.addFutureAction('$location.search()', function($window, $document, done) {
39776 done(null, $document.injector().get('$location').search());
39780 api.hash = function() {
39781 return this.addFutureAction('$location.hash()', function($window, $document, done) {
39782 done(null, $document.injector().get('$location').hash());
39789 return function() {
39796 * expect(future).{matcher} where matcher is one of the matchers defined
39797 * with angular.scenario.matcher
39799 * ex. expect(binding("name")).toEqual("Elliott")
39801 angular.scenario.dsl('expect', function() {
39802 var chain = angular.extend({}, angular.scenario.matcher);
39804 chain.not = function() {
39805 this.inverse = true;
39809 return function(future) {
39810 this.future = future;
39817 * using(selector, label) scopes the next DSL element selection
39820 * using('#foo', "'Foo' text field").input('bar')
39822 angular.scenario.dsl('using', function() {
39823 return function(selector, label) {
39824 this.selector = _jQuery.trim((this.selector || '') + ' ' + selector);
39825 if (angular.isString(label) && label.length) {
39826 this.label = label + ' ( ' + this.selector + ' )';
39828 this.label = this.selector;
39836 * binding(name) returns the value of the first matching binding
39838 angular.scenario.dsl('binding', function() {
39839 return function(name) {
39840 return this.addFutureAction("select binding '" + name + "'",
39841 function($window, $document, done) {
39842 var values = $document.elements().bindings($window.angular.element, name);
39843 if (!values.length) {
39844 return done("Binding selector '" + name + "' did not match.");
39846 done(null, values[0]);
39853 * input(name).enter(value) enters value in input with specified name
39854 * input(name).check() checks checkbox
39855 * input(name).select(value) selects the radio button with specified name/value
39856 * input(name).val() returns the value of the input.
39858 angular.scenario.dsl('input', function() {
39860 var supportInputEvent = 'oninput' in document.createElement('div') && !(msie && msie <= 11);
39862 chain.enter = function(value, event) {
39863 return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",
39864 function($window, $document, done) {
39865 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
39867 input.trigger(event || (supportInputEvent ? 'input' : 'change'));
39872 chain.check = function() {
39873 return this.addFutureAction("checkbox '" + this.name + "' toggle",
39874 function($window, $document, done) {
39875 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':checkbox');
39876 input.trigger('click');
39881 chain.select = function(value) {
39882 return this.addFutureAction("radio button '" + this.name + "' toggle '" + value + "'",
39883 function($window, $document, done) {
39884 var input = $document.
39885 elements('[ng\\:model="$1"][value="$2"]', this.name, value).filter(':radio');
39886 input.trigger('click');
39891 chain.val = function() {
39892 return this.addFutureAction("return input val", function($window, $document, done) {
39893 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
39894 done(null,input.val());
39898 return function(name) {
39907 * repeater('#products table', 'Product List').count() number of rows
39908 * repeater('#products table', 'Product List').row(1) all bindings in row as an array
39909 * repeater('#products table', 'Product List').column('product.name') all values across all rows
39912 angular.scenario.dsl('repeater', function() {
39915 chain.count = function() {
39916 return this.addFutureAction("repeater '" + this.label + "' count",
39917 function($window, $document, done) {
39919 done(null, $document.elements().length);
39926 chain.column = function(binding) {
39927 return this.addFutureAction("repeater '" + this.label + "' column '" + binding + "'",
39928 function($window, $document, done) {
39929 done(null, $document.elements().bindings($window.angular.element, binding));
39933 chain.row = function(index) {
39934 return this.addFutureAction("repeater '" + this.label + "' row '" + index + "'",
39935 function($window, $document, done) {
39936 var matches = $document.elements().slice(index, index + 1);
39937 if (!matches.length) {
39938 return done('row ' + index + ' out of bounds');
39940 done(null, matches.bindings($window.angular.element));
39944 return function(selector, label) {
39945 this.dsl.using(selector, label);
39952 * select(name).option('value') select one option
39953 * select(name).options('value1', 'value2', ...) select options from a multi select
39955 angular.scenario.dsl('select', function() {
39958 chain.option = function(value) {
39959 return this.addFutureAction("select '" + this.name + "' option '" + value + "'",
39960 function($window, $document, done) {
39961 var select = $document.elements('select[ng\\:model="$1"]', this.name);
39962 var option = select.find('option[value="' + value + '"]');
39963 if (option.length) {
39966 option = select.find('option').filter(function() {
39967 return _jQuery(this).text() === value;
39969 if (!option.length) {
39970 option = select.find('option:contains("' + value + '")');
39972 if (option.length) {
39973 select.val(option.val());
39975 return done("option '" + value + "' not found");
39978 select.trigger('change');
39983 chain.options = function() {
39984 var values = arguments;
39985 return this.addFutureAction("select '" + this.name + "' options '" + values + "'",
39986 function($window, $document, done) {
39987 var select = $document.elements('select[multiple][ng\\:model="$1"]', this.name);
39988 select.val(values);
39989 select.trigger('change');
39994 return function(name) {
40002 * element(selector, label).count() get the number of elements that match selector
40003 * element(selector, label).click() clicks an element
40004 * element(selector, label).mouseover() mouseover an element
40005 * element(selector, label).mousedown() mousedown an element
40006 * element(selector, label).mouseup() mouseup an element
40007 * element(selector, label).query(fn) executes fn(selectedElements, done)
40008 * element(selector, label).{method}() gets the value (as defined by jQuery, ex. val)
40009 * element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val)
40010 * element(selector, label).{method}(key) gets the value (as defined by jQuery, ex. attr)
40011 * element(selector, label).{method}(key, value) sets the value (as defined by jQuery, ex. attr)
40013 angular.scenario.dsl('element', function() {
40014 var KEY_VALUE_METHODS = ['attr', 'css', 'prop'];
40015 var VALUE_METHODS = [
40016 'val', 'text', 'html', 'height', 'innerHeight', 'outerHeight', 'width',
40017 'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset'
40021 chain.count = function() {
40022 return this.addFutureAction("element '" + this.label + "' count",
40023 function($window, $document, done) {
40025 done(null, $document.elements().length);
40032 chain.click = function() {
40033 return this.addFutureAction("element '" + this.label + "' click",
40034 function($window, $document, done) {
40035 var elements = $document.elements();
40036 var href = elements.attr('href');
40037 var eventProcessDefault = elements.trigger('click')[0];
40039 if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) {
40040 this.application.navigateTo(href, function() {
40049 chain.dblclick = function() {
40050 return this.addFutureAction("element '" + this.label + "' dblclick",
40051 function($window, $document, done) {
40052 var elements = $document.elements();
40053 var href = elements.attr('href');
40054 var eventProcessDefault = elements.trigger('dblclick')[0];
40056 if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) {
40057 this.application.navigateTo(href, function() {
40066 chain.mouseover = function() {
40067 return this.addFutureAction("element '" + this.label + "' mouseover",
40068 function($window, $document, done) {
40069 var elements = $document.elements();
40070 elements.trigger('mouseover');
40075 chain.mousedown = function() {
40076 return this.addFutureAction("element '" + this.label + "' mousedown",
40077 function($window, $document, done) {
40078 var elements = $document.elements();
40079 elements.trigger('mousedown');
40084 chain.mouseup = function() {
40085 return this.addFutureAction("element '" + this.label + "' mouseup",
40086 function($window, $document, done) {
40087 var elements = $document.elements();
40088 elements.trigger('mouseup');
40093 chain.query = function(fn) {
40094 return this.addFutureAction('element ' + this.label + ' custom query',
40095 function($window, $document, done) {
40096 fn.call(this, $document.elements(), done);
40100 angular.forEach(KEY_VALUE_METHODS, function(methodName) {
40101 chain[methodName] = function(name, value) {
40102 var args = arguments,
40103 futureName = (args.length == 1)
40104 ? "element '" + this.label + "' get " + methodName + " '" + name + "'"
40105 : "element '" + this.label + "' set " + methodName + " '" + name + "' to " + "'" +
40108 return this.addFutureAction(futureName, function($window, $document, done) {
40109 var element = $document.elements();
40110 done(null, element[methodName].apply(element, args));
40115 angular.forEach(VALUE_METHODS, function(methodName) {
40116 chain[methodName] = function(value) {
40117 var args = arguments,
40118 futureName = (args.length === 0)
40119 ? "element '" + this.label + "' " + methodName
40120 : "element '" + this.label + "' set " + methodName + " to '" + value + "'";
40122 return this.addFutureAction(futureName, function($window, $document, done) {
40123 var element = $document.elements();
40124 done(null, element[methodName].apply(element, args));
40129 return function(selector, label) {
40130 this.dsl.using(selector, label);
40136 * Matchers for implementing specs. Follows the Jasmine spec conventions.
40139 angular.scenario.matcher('toEqual', function(expected) {
40140 return angular.equals(this.actual, expected);
40143 angular.scenario.matcher('toBe', function(expected) {
40144 return this.actual === expected;
40147 angular.scenario.matcher('toBeDefined', function() {
40148 return angular.isDefined(this.actual);
40151 angular.scenario.matcher('toBeTruthy', function() {
40152 return this.actual;
40155 angular.scenario.matcher('toBeFalsy', function() {
40156 return !this.actual;
40159 angular.scenario.matcher('toMatch', function(expected) {
40160 return new RegExp(expected).test(this.actual);
40163 angular.scenario.matcher('toBeNull', function() {
40164 return this.actual === null;
40167 angular.scenario.matcher('toContain', function(expected) {
40168 return includes(this.actual, expected);
40171 angular.scenario.matcher('toBeLessThan', function(expected) {
40172 return this.actual < expected;
40175 angular.scenario.matcher('toBeGreaterThan', function(expected) {
40176 return this.actual > expected;
40180 * User Interface for the Scenario Runner.
40182 * TODO(esprehn): This should be refactored now that ObjectModel exists
40183 * to use angular bindings for the UI.
40185 angular.scenario.output('html', function(context, runner, model) {
40186 var specUiMap = {},
40187 lastStepUiMap = {};
40190 '<div id="header">' +
40191 ' <h1><span class="angular">AngularJS</span>: Scenario Test Runner</h1>' +
40192 ' <ul id="status-legend" class="status-display">' +
40193 ' <li class="status-error">0 Errors</li>' +
40194 ' <li class="status-failure">0 Failures</li>' +
40195 ' <li class="status-success">0 Passed</li>' +
40198 '<div id="specs">' +
40199 ' <div class="test-children"></div>' +
40203 runner.on('InteractivePause', function(spec) {
40204 var ui = lastStepUiMap[spec.id];
40205 ui.find('.test-title').
40206 html('paused... <a href="javascript:resume()">resume</a> when ready.');
40209 runner.on('SpecBegin', function(spec) {
40210 var ui = findContext(spec);
40211 ui.find('> .tests').append(
40212 '<li class="status-pending test-it"></li>'
40214 ui = ui.find('> .tests li:last');
40216 '<div class="test-info">' +
40217 ' <p class="test-title">' +
40218 ' <span class="timer-result"></span>' +
40219 ' <span class="test-name"></span>' +
40222 '<div class="scrollpane">' +
40223 ' <ol class="test-actions"></ol>' +
40226 ui.find('> .test-info .test-name').text(spec.name);
40227 ui.find('> .test-info').click(function() {
40228 var scrollpane = ui.find('> .scrollpane');
40229 var actions = scrollpane.find('> .test-actions');
40230 var name = context.find('> .test-info .test-name');
40231 if (actions.find(':visible').length) {
40233 name.removeClass('open').addClass('closed');
40236 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
40237 name.removeClass('closed').addClass('open');
40241 specUiMap[spec.id] = ui;
40244 runner.on('SpecError', function(spec, error) {
40245 var ui = specUiMap[spec.id];
40246 ui.append('<pre></pre>');
40247 ui.find('> pre').text(formatException(error));
40250 runner.on('SpecEnd', function(spec) {
40251 var ui = specUiMap[spec.id];
40252 spec = model.getSpec(spec.id);
40253 ui.removeClass('status-pending');
40254 ui.addClass('status-' + spec.status);
40255 ui.find("> .test-info .timer-result").text(spec.duration + "ms");
40256 if (spec.status === 'success') {
40257 ui.find('> .test-info .test-name').addClass('closed');
40258 ui.find('> .scrollpane .test-actions').hide();
40260 updateTotals(spec.status);
40263 runner.on('StepBegin', function(spec, step) {
40264 var ui = specUiMap[spec.id];
40265 spec = model.getSpec(spec.id);
40266 step = spec.getLastStep();
40267 ui.find('> .scrollpane .test-actions').append('<li class="status-pending"></li>');
40268 var stepUi = lastStepUiMap[spec.id] = ui.find('> .scrollpane .test-actions li:last');
40270 '<div class="timer-result"></div>' +
40271 '<div class="test-title"></div>'
40273 stepUi.find('> .test-title').text(step.name);
40274 var scrollpane = stepUi.parents('.scrollpane');
40275 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
40278 runner.on('StepFailure', function(spec, step, error) {
40279 var ui = lastStepUiMap[spec.id];
40280 addError(ui, step.line, error);
40283 runner.on('StepError', function(spec, step, error) {
40284 var ui = lastStepUiMap[spec.id];
40285 addError(ui, step.line, error);
40288 runner.on('StepEnd', function(spec, step) {
40289 var stepUi = lastStepUiMap[spec.id];
40290 spec = model.getSpec(spec.id);
40291 step = spec.getLastStep();
40292 stepUi.find('.timer-result').text(step.duration + 'ms');
40293 stepUi.removeClass('status-pending');
40294 stepUi.addClass('status-' + step.status);
40295 var scrollpane = specUiMap[spec.id].find('> .scrollpane');
40296 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
40300 * Finds the context of a spec block defined by the passed definition.
40302 * @param {Object} The definition created by the Describe object.
40304 function findContext(spec) {
40305 var currentContext = context.find('#specs');
40306 angular.forEach(model.getDefinitionPath(spec), function(defn) {
40307 var id = 'describe-' + defn.id;
40308 if (!context.find('#' + id).length) {
40309 currentContext.find('> .test-children').append(
40310 '<div class="test-describe" id="' + id + '">' +
40312 ' <div class="test-children"></div>' +
40313 ' <ul class="tests"></ul>' +
40316 context.find('#' + id).find('> h2').text('describe: ' + defn.name);
40318 currentContext = context.find('#' + id);
40320 return context.find('#describe-' + spec.definition.id);
40324 * Updates the test counter for the status.
40326 * @param {string} the status.
40328 function updateTotals(status) {
40329 var legend = context.find('#status-legend .status-' + status);
40330 var parts = legend.text().split(' ');
40331 var value = (parts[0] * 1) + 1;
40332 legend.text(value + ' ' + parts[1]);
40336 * Add an error to a step.
40338 * @param {Object} The JQuery wrapped context
40339 * @param {function()} fn() that should return the file/line number of the error
40340 * @param {Object} the error.
40342 function addError(context, line, error) {
40343 context.find('.test-title').append('<pre></pre>');
40344 var message = _jQuery.trim(line() + '\n\n' + formatException(error));
40345 context.find('.test-title pre:last').text(message);
40350 * Generates JSON output into a context.
40352 angular.scenario.output('json', function(context, runner, model) {
40353 model.on('RunnerEnd', function() {
40354 context.text(angular.toJson(model.value));
40359 * Generates XML output into a context.
40361 angular.scenario.output('xml', function(context, runner, model) {
40362 var $ = function(args) {return new context.init(args);};
40363 model.on('RunnerEnd', function() {
40364 var scenario = $('<scenario></scenario>');
40365 context.append(scenario);
40366 serializeXml(scenario, model.value);
40370 * Convert the tree into XML.
40372 * @param {Object} context jQuery context to add the XML to.
40373 * @param {Object} tree node to serialize
40375 function serializeXml(context, tree) {
40376 angular.forEach(tree.children, function(child) {
40377 var describeContext = $('<describe></describe>');
40378 describeContext.attr('id', child.id);
40379 describeContext.attr('name', child.name);
40380 context.append(describeContext);
40381 serializeXml(describeContext, child);
40383 var its = $('<its></its>');
40384 context.append(its);
40385 angular.forEach(tree.specs, function(spec) {
40386 var it = $('<it></it>');
40387 it.attr('id', spec.id);
40388 it.attr('name', spec.name);
40389 it.attr('duration', spec.duration);
40390 it.attr('status', spec.status);
40392 angular.forEach(spec.steps, function(step) {
40393 var stepContext = $('<step></step>');
40394 stepContext.attr('name', step.name);
40395 stepContext.attr('duration', step.duration);
40396 stepContext.attr('status', step.status);
40397 it.append(stepContext);
40399 var error = $('<error></error>');
40400 stepContext.append(error);
40401 error.text(formatException(step.error));
40409 * Creates a global value $result with the result of the runner.
40411 angular.scenario.output('object', function(context, runner, model) {
40412 runner.$window.$result = model.value;
40416 publishExternalAPI(angular);
40418 var $runner = new angular.scenario.Runner(window),
40419 scripts = document.getElementsByTagName('script'),
40420 script = scripts[scripts.length - 1],
40423 angular.forEach(script.attributes, function(attr) {
40424 var match = attr.name.match(/ng[:\-](.*)/);
40426 config[match[1]] = attr.value || true;
40430 if (config.autotest) {
40431 JQLite(document).ready(function() {
40432 angular.scenario.setUpAndRun(config);
40435 })(window, document);
40438 !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>');
40439 !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>');