2 * jQuery JavaScript Library v2.1.1
8 * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
9 * Released under the MIT license
10 * http://jquery.org/license
12 * Date: 2014-05-01T17:11Z
15 (function( global, factory ) {'use strict';
17 if ( typeof module === "object" && typeof module.exports === "object" ) {
18 // For CommonJS and CommonJS-like environments where a proper window is present,
19 // execute the factory and get jQuery
20 // For environments that do not inherently posses a window with a document
21 // (such as Node.js), expose a jQuery-making factory as module.exports
22 // This accentuates the need for the creation of a real window
23 // e.g. var jQuery = require("jquery")(window);
24 // See ticket #14549 for more info
25 module.exports = global.document ?
26 factory( global, true ) :
29 throw new Error( "jQuery requires a window with a document" );
37 // Pass this if window is not defined yet
38 }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
40 // Can't do this because several apps including ASP.NET trace
41 // the stack via arguments.caller.callee and Firefox dies if
42 // you try to trace through "use strict" call chains. (#13335)
43 // Support: Firefox 18+
48 var slice = arr.slice;
50 var concat = arr.concat;
54 var indexOf = arr.indexOf;
58 var toString = class2type.toString;
60 var hasOwn = class2type.hasOwnProperty;
67 // Use the correct document accordingly with window argument (sandbox)
68 document = window.document,
72 // Define a local copy of jQuery
73 jQuery = function( selector, context ) {
74 // The jQuery object is actually just the init constructor 'enhanced'
75 // Need init if jQuery is called (just allow error to be thrown if not included)
76 return new jQuery.fn.init( selector, context );
79 // Support: Android<4.1
80 // Make sure we trim BOM and NBSP
81 rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
83 // Matches dashed string for camelizing
85 rdashAlpha = /-([\da-z])/gi,
87 // Used by jQuery.camelCase as callback to replace()
88 fcamelCase = function( all, letter ) {
89 return letter.toUpperCase();
92 jQuery.fn = jQuery.prototype = {
93 // The current version of jQuery being used
98 // Start with an empty selector
101 // The default length of a jQuery object is 0
104 toArray: function() {
105 return slice.call( this );
108 // Get the Nth element in the matched element set OR
109 // Get the whole matched element set as a clean array
110 get: function( num ) {
113 // Return just the one element from the set
114 ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
116 // Return all the elements in a clean array
120 // Take an array of elements and push it onto the stack
121 // (returning the new matched element set)
122 pushStack: function( elems ) {
124 // Build a new jQuery matched element set
125 var ret = jQuery.merge( this.constructor(), elems );
127 // Add the old object onto the stack (as a reference)
128 ret.prevObject = this;
129 ret.context = this.context;
131 // Return the newly-formed element set
135 // Execute a callback for every element in the matched set.
136 // (You can seed the arguments with an array of args, but this is
137 // only used internally.)
138 each: function( callback, args ) {
139 return jQuery.each( this, callback, args );
142 map: function( callback ) {
143 return this.pushStack( jQuery.map(this, function( elem, i ) {
144 return callback.call( elem, i, elem );
149 return this.pushStack( slice.apply( this, arguments ) );
157 return this.eq( -1 );
161 var len = this.length,
162 j = +i + ( i < 0 ? len : 0 );
163 return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
167 return this.prevObject || this.constructor(null);
170 // For internal use only.
171 // Behaves like an Array's method, not like a jQuery method.
177 jQuery.extend = jQuery.fn.extend = function() {
178 var options, name, src, copy, copyIsArray, clone,
179 target = arguments[0] || {},
181 length = arguments.length,
184 // Handle a deep copy situation
185 if ( typeof target === "boolean" ) {
188 // skip the boolean and the target
189 target = arguments[ i ] || {};
193 // Handle case when target is a string or something (possible in deep copy)
194 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
198 // extend jQuery itself if only one argument is passed
199 if ( i === length ) {
204 for ( ; i < length; i++ ) {
205 // Only deal with non-null/undefined values
206 if ( (options = arguments[ i ]) != null ) {
207 // Extend the base object
208 for ( name in options ) {
209 src = target[ name ];
210 copy = options[ name ];
212 // Prevent never-ending loop
213 if ( target === copy ) {
217 // Recurse if we're merging plain objects or arrays
218 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
221 clone = src && jQuery.isArray(src) ? src : [];
224 clone = src && jQuery.isPlainObject(src) ? src : {};
227 // Never move original objects, clone them
228 target[ name ] = jQuery.extend( deep, clone, copy );
230 // Don't bring in undefined values
231 } else if ( copy !== undefined ) {
232 target[ name ] = copy;
238 // Return the modified object
243 // Unique for each copy of jQuery on the page
244 expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
246 // Assume jQuery is ready without the ready module
249 error: function( msg ) {
250 throw new Error( msg );
255 // See test/unit/core.js for details concerning isFunction.
256 // Since version 1.3, DOM methods and functions like alert
257 // aren't supported. They return false on IE (#2968).
258 isFunction: function( obj ) {
259 return jQuery.type(obj) === "function";
262 isArray: Array.isArray,
264 isWindow: function( obj ) {
265 return obj != null && obj === obj.window;
268 isNumeric: function( obj ) {
269 // parseFloat NaNs numeric-cast false positives (null|true|false|"")
270 // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
271 // subtraction forces infinities to NaN
272 return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
275 isPlainObject: function( obj ) {
276 // Not plain objects:
277 // - Any object or value whose internal [[Class]] property is not "[object Object]"
280 if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
284 if ( obj.constructor &&
285 !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
289 // If the function hasn't returned already, we're confident that
290 // |obj| is a plain object, created by {} or constructed with new Object
294 isEmptyObject: function( obj ) {
296 for ( name in obj ) {
302 type: function( obj ) {
306 // Support: Android < 4.0, iOS < 6 (functionish RegExp)
307 return typeof obj === "object" || typeof obj === "function" ?
308 class2type[ toString.call(obj) ] || "object" :
312 // Evaluates a script in a global context
313 globalEval: function( code ) {
317 code = jQuery.trim( code );
320 // If the code includes a valid, prologue position
321 // strict mode pragma, execute code by injecting a
322 // script tag into the document.
323 if ( code.indexOf("use strict") === 1 ) {
324 script = document.createElement("script");
326 document.head.appendChild( script ).parentNode.removeChild( script );
328 // Otherwise, avoid the DOM node creation, insertion
329 // and removal by using an indirect global eval
335 // Convert dashed to camelCase; used by the css and data modules
336 // Microsoft forgot to hump their vendor prefix (#9572)
337 camelCase: function( string ) {
338 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
341 nodeName: function( elem, name ) {
342 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
345 // args is for internal usage only
346 each: function( obj, callback, args ) {
350 isArray = isArraylike( obj );
354 for ( ; i < length; i++ ) {
355 value = callback.apply( obj[ i ], args );
357 if ( value === false ) {
363 value = callback.apply( obj[ i ], args );
365 if ( value === false ) {
371 // A special, fast, case for the most common use of each
374 for ( ; i < length; i++ ) {
375 value = callback.call( obj[ i ], i, obj[ i ] );
377 if ( value === false ) {
383 value = callback.call( obj[ i ], i, obj[ i ] );
385 if ( value === false ) {
395 // Support: Android<4.1
396 trim: function( text ) {
397 return text == null ?
399 ( text + "" ).replace( rtrim, "" );
402 // results is for internal usage only
403 makeArray: function( arr, results ) {
404 var ret = results || [];
407 if ( isArraylike( Object(arr) ) ) {
409 typeof arr === "string" ?
413 push.call( ret, arr );
420 inArray: function( elem, arr, i ) {
421 return arr == null ? -1 : indexOf.call( arr, elem, i );
424 merge: function( first, second ) {
425 var len = +second.length,
429 for ( ; j < len; j++ ) {
430 first[ i++ ] = second[ j ];
438 grep: function( elems, callback, invert ) {
442 length = elems.length,
443 callbackExpect = !invert;
445 // Go through the array, only saving the items
446 // that pass the validator function
447 for ( ; i < length; i++ ) {
448 callbackInverse = !callback( elems[ i ], i );
449 if ( callbackInverse !== callbackExpect ) {
450 matches.push( elems[ i ] );
457 // arg is for internal usage only
458 map: function( elems, callback, arg ) {
461 length = elems.length,
462 isArray = isArraylike( elems ),
465 // Go through the array, translating each of the items to their new values
467 for ( ; i < length; i++ ) {
468 value = callback( elems[ i ], i, arg );
470 if ( value != null ) {
475 // Go through every key on the object,
478 value = callback( elems[ i ], i, arg );
480 if ( value != null ) {
486 // Flatten any nested arrays
487 return concat.apply( [], ret );
490 // A global GUID counter for objects
493 // Bind a function to a context, optionally partially applying any
495 proxy: function( fn, context ) {
496 var tmp, args, proxy;
498 if ( typeof context === "string" ) {
504 // Quick check to determine if target is callable, in the spec
505 // this throws a TypeError, but we will just return undefined.
506 if ( !jQuery.isFunction( fn ) ) {
511 args = slice.call( arguments, 2 );
513 return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
516 // Set the guid of unique handler to the same of original handler, so it can be removed
517 proxy.guid = fn.guid = fn.guid || jQuery.guid++;
524 // jQuery.support is not used in Core but other projects attach their
525 // properties to it so it needs to exist.
529 // Populate the class2type map
530 jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
531 class2type[ "[object " + name + "]" ] = name.toLowerCase();
534 function isArraylike( obj ) {
535 var length = obj.length,
536 type = jQuery.type( obj );
538 if ( type === "function" || jQuery.isWindow( obj ) ) {
542 if ( obj.nodeType === 1 && length ) {
546 return type === "array" || length === 0 ||
547 typeof length === "number" && length > 0 && ( length - 1 ) in obj;
551 * Sizzle CSS Selector Engine v1.10.19
552 * http://sizzlejs.com/
554 * Copyright 2013 jQuery Foundation, Inc. and other contributors
555 * Released under the MIT license
556 * http://jquery.org/license
560 (function( window ) {
574 // Local document vars
584 // Instance-specific data
585 expando = "sizzle" + -(new Date()),
586 preferredDoc = window.document,
589 classCache = createCache(),
590 tokenCache = createCache(),
591 compilerCache = createCache(),
592 sortOrder = function( a, b ) {
599 // General-purpose constants
600 strundefined = typeof undefined,
601 MAX_NEGATIVE = 1 << 31,
604 hasOwn = ({}).hasOwnProperty,
607 push_native = arr.push,
610 // Use a stripped-down indexOf if we can't use a native one
611 indexOf = arr.indexOf || function( elem ) {
614 for ( ; i < len; i++ ) {
615 if ( this[i] === elem ) {
622 booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
624 // Regular expressions
626 // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
627 whitespace = "[\\x20\\t\\r\\n\\f]",
628 // http://www.w3.org/TR/css3-syntax/#characters
629 characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
631 // Loosely modeled on CSS identifier characters
632 // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
633 // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
634 identifier = characterEncoding.replace( "w", "w#" ),
636 // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
637 attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
638 // Operator (capture 2)
639 "*([*^$|!~]?=)" + whitespace +
640 // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
641 "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
644 pseudos = ":(" + characterEncoding + ")(?:\\((" +
645 // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
646 // 1. quoted (capture 3; capture 4 or capture 5)
647 "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
648 // 2. simple (capture 6)
649 "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
650 // 3. anything else (capture 2)
654 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
655 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
657 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
658 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
660 rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
662 rpseudo = new RegExp( pseudos ),
663 ridentifier = new RegExp( "^" + identifier + "$" ),
666 "ID": new RegExp( "^#(" + characterEncoding + ")" ),
667 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
668 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
669 "ATTR": new RegExp( "^" + attributes ),
670 "PSEUDO": new RegExp( "^" + pseudos ),
671 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
672 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
673 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
674 "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
675 // For use in libraries implementing .is()
676 // We use this for POS matching in `select`
677 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
678 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
681 rinputs = /^(?:input|select|textarea|button)$/i,
684 rnative = /^[^{]+\{\s*\[native \w/,
686 // Easily-parseable/retrievable ID or TAG or CLASS selectors
687 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
692 // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
693 runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
694 funescape = function( _, escaped, escapedWhitespace ) {
695 var high = "0x" + escaped - 0x10000;
696 // NaN means non-codepoint
697 // Support: Firefox<24
698 // Workaround erroneous numeric interpretation of +"0x"
699 return high !== high || escapedWhitespace ?
703 String.fromCharCode( high + 0x10000 ) :
704 // Supplemental Plane codepoint (surrogate pair)
705 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
708 // Optimize for push.apply( _, NodeList )
711 (arr = slice.call( preferredDoc.childNodes )),
712 preferredDoc.childNodes
714 // Support: Android<4.0
715 // Detect silently failing push.apply
716 arr[ preferredDoc.childNodes.length ].nodeType;
718 push = { apply: arr.length ?
720 // Leverage slice if possible
721 function( target, els ) {
722 push_native.apply( target, slice.call(els) );
726 // Otherwise append directly
727 function( target, els ) {
728 var j = target.length,
730 // Can't trust NodeList.length
731 while ( (target[j++] = els[i++]) ) {}
732 target.length = j - 1;
737 function Sizzle( selector, context, results, seed ) {
738 var match, elem, m, nodeType,
740 i, groups, old, nid, newContext, newSelector;
742 if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
743 setDocument( context );
746 context = context || document;
747 results = results || [];
749 if ( !selector || typeof selector !== "string" ) {
753 if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
757 if ( documentIsHTML && !seed ) {
760 if ( (match = rquickExpr.exec( selector )) ) {
761 // Speed-up: Sizzle("#ID")
762 if ( (m = match[1]) ) {
763 if ( nodeType === 9 ) {
764 elem = context.getElementById( m );
765 // Check parentNode to catch when Blackberry 4.6 returns
766 // nodes that are no longer in the document (jQuery #6963)
767 if ( elem && elem.parentNode ) {
768 // Handle the case where IE, Opera, and Webkit return items
769 // by name instead of ID
770 if ( elem.id === m ) {
771 results.push( elem );
778 // Context is not a document
779 if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
780 contains( context, elem ) && elem.id === m ) {
781 results.push( elem );
786 // Speed-up: Sizzle("TAG")
787 } else if ( match[2] ) {
788 push.apply( results, context.getElementsByTagName( selector ) );
791 // Speed-up: Sizzle(".CLASS")
792 } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
793 push.apply( results, context.getElementsByClassName( m ) );
799 if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
801 newContext = context;
802 newSelector = nodeType === 9 && selector;
804 // qSA works strangely on Element-rooted queries
805 // We can work around this by specifying an extra ID on the root
806 // and working up from there (Thanks to Andrew Dupont for the technique)
807 // IE 8 doesn't work on object elements
808 if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
809 groups = tokenize( selector );
811 if ( (old = context.getAttribute("id")) ) {
812 nid = old.replace( rescape, "\\$&" );
814 context.setAttribute( "id", nid );
816 nid = "[id='" + nid + "'] ";
820 groups[i] = nid + toSelector( groups[i] );
822 newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
823 newSelector = groups.join(",");
829 newContext.querySelectorAll( newSelector )
835 context.removeAttribute("id");
843 return select( selector.replace( rtrim, "$1" ), context, results, seed );
847 * Create key-value caches of limited size
848 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
849 * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
850 * deleting the oldest entry
852 function createCache() {
855 function cache( key, value ) {
856 // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
857 if ( keys.push( key + " " ) > Expr.cacheLength ) {
858 // Only keep the most recent entries
859 delete cache[ keys.shift() ];
861 return (cache[ key + " " ] = value);
867 * Mark a function for special use by Sizzle
868 * @param {Function} fn The function to mark
870 function markFunction( fn ) {
871 fn[ expando ] = true;
876 * Support testing using an element
877 * @param {Function} fn Passed the created div and expects a boolean result
879 function assert( fn ) {
880 var div = document.createElement("div");
887 // Remove from its parent by default
888 if ( div.parentNode ) {
889 div.parentNode.removeChild( div );
891 // release memory in IE
897 * Adds the same handler for all of the specified attrs
898 * @param {String} attrs Pipe-separated list of attributes
899 * @param {Function} handler The method that will be applied
901 function addHandle( attrs, handler ) {
902 var arr = attrs.split("|"),
906 Expr.attrHandle[ arr[i] ] = handler;
911 * Checks document order of two siblings
914 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
916 function siblingCheck( a, b ) {
918 diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
919 ( ~b.sourceIndex || MAX_NEGATIVE ) -
920 ( ~a.sourceIndex || MAX_NEGATIVE );
922 // Use IE sourceIndex if available on both nodes
927 // Check if b follows a
929 while ( (cur = cur.nextSibling) ) {
940 * Returns a function to use in pseudos for input types
941 * @param {String} type
943 function createInputPseudo( type ) {
944 return function( elem ) {
945 var name = elem.nodeName.toLowerCase();
946 return name === "input" && elem.type === type;
951 * Returns a function to use in pseudos for buttons
952 * @param {String} type
954 function createButtonPseudo( type ) {
955 return function( elem ) {
956 var name = elem.nodeName.toLowerCase();
957 return (name === "input" || name === "button") && elem.type === type;
962 * Returns a function to use in pseudos for positionals
963 * @param {Function} fn
965 function createPositionalPseudo( fn ) {
966 return markFunction(function( argument ) {
967 argument = +argument;
968 return markFunction(function( seed, matches ) {
970 matchIndexes = fn( [], seed.length, argument ),
971 i = matchIndexes.length;
973 // Match elements found at the specified indexes
975 if ( seed[ (j = matchIndexes[i]) ] ) {
976 seed[j] = !(matches[j] = seed[j]);
984 * Checks a node for validity as a Sizzle context
985 * @param {Element|Object=} context
986 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
988 function testContext( context ) {
989 return context && typeof context.getElementsByTagName !== strundefined && context;
992 // Expose support vars for convenience
993 support = Sizzle.support = {};
997 * @param {Element|Object} elem An element or a document
998 * @returns {Boolean} True iff elem is a non-HTML XML node
1000 isXML = Sizzle.isXML = function( elem ) {
1001 // documentElement is verified for cases where it doesn't yet exist
1002 // (such as loading iframes in IE - #4833)
1003 var documentElement = elem && (elem.ownerDocument || elem).documentElement;
1004 return documentElement ? documentElement.nodeName !== "HTML" : false;
1008 * Sets document-related variables once based on the current document
1009 * @param {Element|Object} [doc] An element or document object to use to set the document
1010 * @returns {Object} Returns the current document
1012 setDocument = Sizzle.setDocument = function( node ) {
1014 doc = node ? node.ownerDocument || node : preferredDoc,
1015 parent = doc.defaultView;
1017 // If no document and documentElement is available, return
1018 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
1024 docElem = doc.documentElement;
1027 documentIsHTML = !isXML( doc );
1030 // If iframe document is assigned to "document" variable and if iframe has been reloaded,
1031 // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
1032 // IE6-8 do not support the defaultView property so parent will be undefined
1033 if ( parent && parent !== parent.top ) {
1034 // IE11 does not have attachEvent, so all must suffer
1035 if ( parent.addEventListener ) {
1036 parent.addEventListener( "unload", function() {
1039 } else if ( parent.attachEvent ) {
1040 parent.attachEvent( "onunload", function() {
1047 ---------------------------------------------------------------------- */
1050 // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
1051 support.attributes = assert(function( div ) {
1052 div.className = "i";
1053 return !div.getAttribute("className");
1057 ---------------------------------------------------------------------- */
1059 // Check if getElementsByTagName("*") returns only elements
1060 support.getElementsByTagName = assert(function( div ) {
1061 div.appendChild( doc.createComment("") );
1062 return !div.getElementsByTagName("*").length;
1065 // Check if getElementsByClassName can be trusted
1066 support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
1067 div.innerHTML = "<div class='a'></div><div class='a i'></div>";
1069 // Support: Safari<4
1070 // Catch class over-caching
1071 div.firstChild.className = "i";
1072 // Support: Opera<10
1073 // Catch gEBCN failure to find non-leading classes
1074 return div.getElementsByClassName("i").length === 2;
1078 // Check if getElementById returns elements by name
1079 // The broken getElementById methods don't pick up programatically-set names,
1080 // so use a roundabout getElementsByName test
1081 support.getById = assert(function( div ) {
1082 docElem.appendChild( div ).id = expando;
1083 return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
1086 // ID find and filter
1087 if ( support.getById ) {
1088 Expr.find["ID"] = function( id, context ) {
1089 if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
1090 var m = context.getElementById( id );
1091 // Check parentNode to catch when Blackberry 4.6 returns
1092 // nodes that are no longer in the document #6963
1093 return m && m.parentNode ? [ m ] : [];
1096 Expr.filter["ID"] = function( id ) {
1097 var attrId = id.replace( runescape, funescape );
1098 return function( elem ) {
1099 return elem.getAttribute("id") === attrId;
1104 // getElementById is not reliable as a find shortcut
1105 delete Expr.find["ID"];
1107 Expr.filter["ID"] = function( id ) {
1108 var attrId = id.replace( runescape, funescape );
1109 return function( elem ) {
1110 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
1111 return node && node.value === attrId;
1117 Expr.find["TAG"] = support.getElementsByTagName ?
1118 function( tag, context ) {
1119 if ( typeof context.getElementsByTagName !== strundefined ) {
1120 return context.getElementsByTagName( tag );
1123 function( tag, context ) {
1127 results = context.getElementsByTagName( tag );
1129 // Filter out possible comments
1130 if ( tag === "*" ) {
1131 while ( (elem = results[i++]) ) {
1132 if ( elem.nodeType === 1 ) {
1143 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
1144 if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
1145 return context.getElementsByClassName( className );
1149 /* QSA/matchesSelector
1150 ---------------------------------------------------------------------- */
1152 // QSA and matchesSelector support
1154 // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
1157 // qSa(:focus) reports false when true (Chrome 21)
1158 // We allow this because of a bug in IE8/9 that throws an error
1159 // whenever `document.activeElement` is accessed on an iframe
1160 // So, we allow :focus to pass through QSA all the time to avoid the IE error
1161 // See http://bugs.jquery.com/ticket/13378
1164 if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
1166 // Regex strategy adopted from Diego Perini
1167 assert(function( div ) {
1168 // Select is set to empty string on purpose
1169 // This is to test IE's treatment of not explicitly
1170 // setting a boolean content attribute,
1171 // since its presence should be enough
1172 // http://bugs.jquery.com/ticket/12359
1173 div.innerHTML = "<select msallowclip=''><option selected=''></option></select>";
1175 // Support: IE8, Opera 11-12.16
1176 // Nothing should be selected when empty strings follow ^= or $= or *=
1177 // The test attribute must be unknown in Opera but "safe" for WinRT
1178 // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
1179 if ( div.querySelectorAll("[msallowclip^='']").length ) {
1180 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
1184 // Boolean attributes and "value" are not treated correctly
1185 if ( !div.querySelectorAll("[selected]").length ) {
1186 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
1189 // Webkit/Opera - :checked should return selected option elements
1190 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1191 // IE8 throws error here and will not see later tests
1192 if ( !div.querySelectorAll(":checked").length ) {
1193 rbuggyQSA.push(":checked");
1197 assert(function( div ) {
1198 // Support: Windows 8 Native Apps
1199 // The type and name attributes are restricted during .innerHTML assignment
1200 var input = doc.createElement("input");
1201 input.setAttribute( "type", "hidden" );
1202 div.appendChild( input ).setAttribute( "name", "D" );
1205 // Enforce case-sensitivity of name attribute
1206 if ( div.querySelectorAll("[name=d]").length ) {
1207 rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
1210 // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
1211 // IE8 throws error here and will not see later tests
1212 if ( !div.querySelectorAll(":enabled").length ) {
1213 rbuggyQSA.push( ":enabled", ":disabled" );
1216 // Opera 10-11 does not throw on post-comma invalid pseudos
1217 div.querySelectorAll("*,:x");
1218 rbuggyQSA.push(",.*:");
1222 if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
1223 docElem.webkitMatchesSelector ||
1224 docElem.mozMatchesSelector ||
1225 docElem.oMatchesSelector ||
1226 docElem.msMatchesSelector) )) ) {
1228 assert(function( div ) {
1229 // Check to see if it's possible to do matchesSelector
1230 // on a disconnected node (IE 9)
1231 support.disconnectedMatch = matches.call( div, "div" );
1233 // This should fail with an exception
1234 // Gecko does not error, returns false instead
1235 matches.call( div, "[s!='']:x" );
1236 rbuggyMatches.push( "!=", pseudos );
1240 rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
1241 rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
1244 ---------------------------------------------------------------------- */
1245 hasCompare = rnative.test( docElem.compareDocumentPosition );
1247 // Element contains another
1248 // Purposefully does not implement inclusive descendent
1249 // As in, an element does not contain itself
1250 contains = hasCompare || rnative.test( docElem.contains ) ?
1252 var adown = a.nodeType === 9 ? a.documentElement : a,
1253 bup = b && b.parentNode;
1254 return a === bup || !!( bup && bup.nodeType === 1 && (
1256 adown.contains( bup ) :
1257 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
1262 while ( (b = b.parentNode) ) {
1272 ---------------------------------------------------------------------- */
1274 // Document order sorting
1275 sortOrder = hasCompare ?
1278 // Flag for duplicate removal
1280 hasDuplicate = true;
1284 // Sort on method existence if only one input has compareDocumentPosition
1285 var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
1290 // Calculate position if both inputs belong to the same document
1291 compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
1292 a.compareDocumentPosition( b ) :
1294 // Otherwise we know they are disconnected
1297 // Disconnected nodes
1299 (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
1301 // Choose the first element that is related to our preferred document
1302 if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
1305 if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
1309 // Maintain original order
1311 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1315 return compare & 4 ? -1 : 1;
1318 // Exit early if the nodes are identical
1320 hasDuplicate = true;
1331 // Parentless nodes are either documents or disconnected
1332 if ( !aup || !bup ) {
1333 return a === doc ? -1 :
1338 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1341 // If the nodes are siblings, we can do a quick check
1342 } else if ( aup === bup ) {
1343 return siblingCheck( a, b );
1346 // Otherwise we need full lists of their ancestors for comparison
1348 while ( (cur = cur.parentNode) ) {
1352 while ( (cur = cur.parentNode) ) {
1356 // Walk down the tree looking for a discrepancy
1357 while ( ap[i] === bp[i] ) {
1362 // Do a sibling check if the nodes have a common ancestor
1363 siblingCheck( ap[i], bp[i] ) :
1365 // Otherwise nodes in our document sort first
1366 ap[i] === preferredDoc ? -1 :
1367 bp[i] === preferredDoc ? 1 :
1374 Sizzle.matches = function( expr, elements ) {
1375 return Sizzle( expr, null, null, elements );
1378 Sizzle.matchesSelector = function( elem, expr ) {
1379 // Set document vars if needed
1380 if ( ( elem.ownerDocument || elem ) !== document ) {
1381 setDocument( elem );
1384 // Make sure that attribute selectors are quoted
1385 expr = expr.replace( rattributeQuotes, "='$1']" );
1387 if ( support.matchesSelector && documentIsHTML &&
1388 ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
1389 ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
1392 var ret = matches.call( elem, expr );
1394 // IE 9's matchesSelector returns false on disconnected nodes
1395 if ( ret || support.disconnectedMatch ||
1396 // As well, disconnected nodes are said to be in a document
1398 elem.document && elem.document.nodeType !== 11 ) {
1404 return Sizzle( expr, document, null, [ elem ] ).length > 0;
1407 Sizzle.contains = function( context, elem ) {
1408 // Set document vars if needed
1409 if ( ( context.ownerDocument || context ) !== document ) {
1410 setDocument( context );
1412 return contains( context, elem );
1415 Sizzle.attr = function( elem, name ) {
1416 // Set document vars if needed
1417 if ( ( elem.ownerDocument || elem ) !== document ) {
1418 setDocument( elem );
1421 var fn = Expr.attrHandle[ name.toLowerCase() ],
1422 // Don't get fooled by Object.prototype properties (jQuery #13807)
1423 val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
1424 fn( elem, name, !documentIsHTML ) :
1427 return val !== undefined ?
1429 support.attributes || !documentIsHTML ?
1430 elem.getAttribute( name ) :
1431 (val = elem.getAttributeNode(name)) && val.specified ?
1436 Sizzle.error = function( msg ) {
1437 throw new Error( "Syntax error, unrecognized expression: " + msg );
1441 * Document sorting and removing duplicates
1442 * @param {ArrayLike} results
1444 Sizzle.uniqueSort = function( results ) {
1450 // Unless we *know* we can detect duplicates, assume their presence
1451 hasDuplicate = !support.detectDuplicates;
1452 sortInput = !support.sortStable && results.slice( 0 );
1453 results.sort( sortOrder );
1455 if ( hasDuplicate ) {
1456 while ( (elem = results[i++]) ) {
1457 if ( elem === results[ i ] ) {
1458 j = duplicates.push( i );
1462 results.splice( duplicates[ j ], 1 );
1466 // Clear input after sorting to release objects
1467 // See https://github.com/jquery/sizzle/pull/225
1474 * Utility function for retrieving the text value of an array of DOM nodes
1475 * @param {Array|Element} elem
1477 getText = Sizzle.getText = function( elem ) {
1481 nodeType = elem.nodeType;
1484 // If no nodeType, this is expected to be an array
1485 while ( (node = elem[i++]) ) {
1486 // Do not traverse comment nodes
1487 ret += getText( node );
1489 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
1490 // Use textContent for elements
1491 // innerText usage removed for consistency of new lines (jQuery #11153)
1492 if ( typeof elem.textContent === "string" ) {
1493 return elem.textContent;
1495 // Traverse its children
1496 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1497 ret += getText( elem );
1500 } else if ( nodeType === 3 || nodeType === 4 ) {
1501 return elem.nodeValue;
1503 // Do not include comment or processing instruction nodes
1508 Expr = Sizzle.selectors = {
1510 // Can be adjusted by the user
1513 createPseudo: markFunction,
1522 ">": { dir: "parentNode", first: true },
1523 " ": { dir: "parentNode" },
1524 "+": { dir: "previousSibling", first: true },
1525 "~": { dir: "previousSibling" }
1529 "ATTR": function( match ) {
1530 match[1] = match[1].replace( runescape, funescape );
1532 // Move the given value to match[3] whether quoted or unquoted
1533 match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
1535 if ( match[2] === "~=" ) {
1536 match[3] = " " + match[3] + " ";
1539 return match.slice( 0, 4 );
1542 "CHILD": function( match ) {
1543 /* matches from matchExpr["CHILD"]
1544 1 type (only|nth|...)
1545 2 what (child|of-type)
1546 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
1547 4 xn-component of xn+y argument ([+-]?\d*n|)
1548 5 sign of xn-component
1550 7 sign of y-component
1553 match[1] = match[1].toLowerCase();
1555 if ( match[1].slice( 0, 3 ) === "nth" ) {
1556 // nth-* requires argument
1558 Sizzle.error( match[0] );
1561 // numeric x and y parameters for Expr.filter.CHILD
1562 // remember that false/true cast respectively to 0/1
1563 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
1564 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
1566 // other types prohibit arguments
1567 } else if ( match[3] ) {
1568 Sizzle.error( match[0] );
1574 "PSEUDO": function( match ) {
1576 unquoted = !match[6] && match[2];
1578 if ( matchExpr["CHILD"].test( match[0] ) ) {
1582 // Accept quoted arguments as-is
1584 match[2] = match[4] || match[5] || "";
1586 // Strip excess characters from unquoted arguments
1587 } else if ( unquoted && rpseudo.test( unquoted ) &&
1588 // Get excess from tokenize (recursively)
1589 (excess = tokenize( unquoted, true )) &&
1590 // advance to the next closing parenthesis
1591 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
1593 // excess is a negative index
1594 match[0] = match[0].slice( 0, excess );
1595 match[2] = unquoted.slice( 0, excess );
1598 // Return only captures needed by the pseudo filter method (type and argument)
1599 return match.slice( 0, 3 );
1605 "TAG": function( nodeNameSelector ) {
1606 var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
1607 return nodeNameSelector === "*" ?
1608 function() { return true; } :
1610 return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
1614 "CLASS": function( className ) {
1615 var pattern = classCache[ className + " " ];
1618 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
1619 classCache( className, function( elem ) {
1620 return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
1624 "ATTR": function( name, operator, check ) {
1625 return function( elem ) {
1626 var result = Sizzle.attr( elem, name );
1628 if ( result == null ) {
1629 return operator === "!=";
1637 return operator === "=" ? result === check :
1638 operator === "!=" ? result !== check :
1639 operator === "^=" ? check && result.indexOf( check ) === 0 :
1640 operator === "*=" ? check && result.indexOf( check ) > -1 :
1641 operator === "$=" ? check && result.slice( -check.length ) === check :
1642 operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
1643 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
1648 "CHILD": function( type, what, argument, first, last ) {
1649 var simple = type.slice( 0, 3 ) !== "nth",
1650 forward = type.slice( -4 ) !== "last",
1651 ofType = what === "of-type";
1653 return first === 1 && last === 0 ?
1655 // Shortcut for :nth-*(n)
1657 return !!elem.parentNode;
1660 function( elem, context, xml ) {
1661 var cache, outerCache, node, diff, nodeIndex, start,
1662 dir = simple !== forward ? "nextSibling" : "previousSibling",
1663 parent = elem.parentNode,
1664 name = ofType && elem.nodeName.toLowerCase(),
1665 useCache = !xml && !ofType;
1669 // :(first|last|only)-(child|of-type)
1673 while ( (node = node[ dir ]) ) {
1674 if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
1678 // Reverse direction for :only-* (if we haven't yet done so)
1679 start = dir = type === "only" && !start && "nextSibling";
1684 start = [ forward ? parent.firstChild : parent.lastChild ];
1686 // non-xml :nth-child(...) stores cache data on `parent`
1687 if ( forward && useCache ) {
1688 // Seek `elem` from a previously-cached index
1689 outerCache = parent[ expando ] || (parent[ expando ] = {});
1690 cache = outerCache[ type ] || [];
1691 nodeIndex = cache[0] === dirruns && cache[1];
1692 diff = cache[0] === dirruns && cache[2];
1693 node = nodeIndex && parent.childNodes[ nodeIndex ];
1695 while ( (node = ++nodeIndex && node && node[ dir ] ||
1697 // Fallback to seeking `elem` from the start
1698 (diff = nodeIndex = 0) || start.pop()) ) {
1700 // When found, cache indexes on `parent` and break
1701 if ( node.nodeType === 1 && ++diff && node === elem ) {
1702 outerCache[ type ] = [ dirruns, nodeIndex, diff ];
1707 // Use previously-cached element index if available
1708 } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
1711 // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
1713 // Use the same loop as above to seek `elem` from the start
1714 while ( (node = ++nodeIndex && node && node[ dir ] ||
1715 (diff = nodeIndex = 0) || start.pop()) ) {
1717 if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
1718 // Cache the index of each encountered element
1720 (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
1723 if ( node === elem ) {
1730 // Incorporate the offset, then check against cycle size
1732 return diff === first || ( diff % first === 0 && diff / first >= 0 );
1737 "PSEUDO": function( pseudo, argument ) {
1738 // pseudo-class names are case-insensitive
1739 // http://www.w3.org/TR/selectors/#pseudo-classes
1740 // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
1741 // Remember that setFilters inherits from pseudos
1743 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
1744 Sizzle.error( "unsupported pseudo: " + pseudo );
1746 // The user may use createPseudo to indicate that
1747 // arguments are needed to create the filter function
1748 // just as Sizzle does
1749 if ( fn[ expando ] ) {
1750 return fn( argument );
1753 // But maintain support for old signatures
1754 if ( fn.length > 1 ) {
1755 args = [ pseudo, pseudo, "", argument ];
1756 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
1757 markFunction(function( seed, matches ) {
1759 matched = fn( seed, argument ),
1762 idx = indexOf.call( seed, matched[i] );
1763 seed[ idx ] = !( matches[ idx ] = matched[i] );
1767 return fn( elem, 0, args );
1776 // Potentially complex pseudos
1777 "not": markFunction(function( selector ) {
1778 // Trim the selector passed to compile
1779 // to avoid treating leading and trailing
1780 // spaces as combinators
1783 matcher = compile( selector.replace( rtrim, "$1" ) );
1785 return matcher[ expando ] ?
1786 markFunction(function( seed, matches, context, xml ) {
1788 unmatched = matcher( seed, null, xml, [] ),
1791 // Match elements unmatched by `matcher`
1793 if ( (elem = unmatched[i]) ) {
1794 seed[i] = !(matches[i] = elem);
1798 function( elem, context, xml ) {
1800 matcher( input, null, xml, results );
1801 return !results.pop();
1805 "has": markFunction(function( selector ) {
1806 return function( elem ) {
1807 return Sizzle( selector, elem ).length > 0;
1811 "contains": markFunction(function( text ) {
1812 return function( elem ) {
1813 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
1817 // "Whether an element is represented by a :lang() selector
1818 // is based solely on the element's language value
1819 // being equal to the identifier C,
1820 // or beginning with the identifier C immediately followed by "-".
1821 // The matching of C against the element's language value is performed case-insensitively.
1822 // The identifier C does not have to be a valid language name."
1823 // http://www.w3.org/TR/selectors/#lang-pseudo
1824 "lang": markFunction( function( lang ) {
1825 // lang value must be a valid identifier
1826 if ( !ridentifier.test(lang || "") ) {
1827 Sizzle.error( "unsupported lang: " + lang );
1829 lang = lang.replace( runescape, funescape ).toLowerCase();
1830 return function( elem ) {
1833 if ( (elemLang = documentIsHTML ?
1835 elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
1837 elemLang = elemLang.toLowerCase();
1838 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
1840 } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
1846 "target": function( elem ) {
1847 var hash = window.location && window.location.hash;
1848 return hash && hash.slice( 1 ) === elem.id;
1851 "root": function( elem ) {
1852 return elem === docElem;
1855 "focus": function( elem ) {
1856 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
1859 // Boolean properties
1860 "enabled": function( elem ) {
1861 return elem.disabled === false;
1864 "disabled": function( elem ) {
1865 return elem.disabled === true;
1868 "checked": function( elem ) {
1869 // In CSS3, :checked should return both checked and selected elements
1870 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1871 var nodeName = elem.nodeName.toLowerCase();
1872 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
1875 "selected": function( elem ) {
1876 // Accessing this property makes selected-by-default
1877 // options in Safari work properly
1878 if ( elem.parentNode ) {
1879 elem.parentNode.selectedIndex;
1882 return elem.selected === true;
1886 "empty": function( elem ) {
1887 // http://www.w3.org/TR/selectors/#empty-pseudo
1888 // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
1889 // but not by others (comment: 8; processing instruction: 7; etc.)
1890 // nodeType < 6 works because attributes (2) do not appear as children
1891 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1892 if ( elem.nodeType < 6 ) {
1899 "parent": function( elem ) {
1900 return !Expr.pseudos["empty"]( elem );
1903 // Element/input types
1904 "header": function( elem ) {
1905 return rheader.test( elem.nodeName );
1908 "input": function( elem ) {
1909 return rinputs.test( elem.nodeName );
1912 "button": function( elem ) {
1913 var name = elem.nodeName.toLowerCase();
1914 return name === "input" && elem.type === "button" || name === "button";
1917 "text": function( elem ) {
1919 return elem.nodeName.toLowerCase() === "input" &&
1920 elem.type === "text" &&
1923 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
1924 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
1927 // Position-in-collection
1928 "first": createPositionalPseudo(function() {
1932 "last": createPositionalPseudo(function( matchIndexes, length ) {
1933 return [ length - 1 ];
1936 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
1937 return [ argument < 0 ? argument + length : argument ];
1940 "even": createPositionalPseudo(function( matchIndexes, length ) {
1942 for ( ; i < length; i += 2 ) {
1943 matchIndexes.push( i );
1945 return matchIndexes;
1948 "odd": createPositionalPseudo(function( matchIndexes, length ) {
1950 for ( ; i < length; i += 2 ) {
1951 matchIndexes.push( i );
1953 return matchIndexes;
1956 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
1957 var i = argument < 0 ? argument + length : argument;
1958 for ( ; --i >= 0; ) {
1959 matchIndexes.push( i );
1961 return matchIndexes;
1964 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
1965 var i = argument < 0 ? argument + length : argument;
1966 for ( ; ++i < length; ) {
1967 matchIndexes.push( i );
1969 return matchIndexes;
1974 Expr.pseudos["nth"] = Expr.pseudos["eq"];
1976 // Add button/input type pseudos
1977 for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
1978 Expr.pseudos[ i ] = createInputPseudo( i );
1980 for ( i in { submit: true, reset: true } ) {
1981 Expr.pseudos[ i ] = createButtonPseudo( i );
1984 // Easy API for creating new setFilters
1985 function setFilters() {}
1986 setFilters.prototype = Expr.filters = Expr.pseudos;
1987 Expr.setFilters = new setFilters();
1989 tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
1990 var matched, match, tokens, type,
1991 soFar, groups, preFilters,
1992 cached = tokenCache[ selector + " " ];
1995 return parseOnly ? 0 : cached.slice( 0 );
2000 preFilters = Expr.preFilter;
2004 // Comma and first run
2005 if ( !matched || (match = rcomma.exec( soFar )) ) {
2007 // Don't consume trailing commas as valid
2008 soFar = soFar.slice( match[0].length ) || soFar;
2010 groups.push( (tokens = []) );
2016 if ( (match = rcombinators.exec( soFar )) ) {
2017 matched = match.shift();
2020 // Cast descendant combinators to space
2021 type: match[0].replace( rtrim, " " )
2023 soFar = soFar.slice( matched.length );
2027 for ( type in Expr.filter ) {
2028 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
2029 (match = preFilters[ type ]( match ))) ) {
2030 matched = match.shift();
2036 soFar = soFar.slice( matched.length );
2045 // Return the length of the invalid excess
2046 // if we're just parsing
2047 // Otherwise, throw an error or return tokens
2051 Sizzle.error( selector ) :
2053 tokenCache( selector, groups ).slice( 0 );
2056 function toSelector( tokens ) {
2058 len = tokens.length,
2060 for ( ; i < len; i++ ) {
2061 selector += tokens[i].value;
2066 function addCombinator( matcher, combinator, base ) {
2067 var dir = combinator.dir,
2068 checkNonElements = base && dir === "parentNode",
2071 return combinator.first ?
2072 // Check against closest ancestor/preceding element
2073 function( elem, context, xml ) {
2074 while ( (elem = elem[ dir ]) ) {
2075 if ( elem.nodeType === 1 || checkNonElements ) {
2076 return matcher( elem, context, xml );
2081 // Check against all ancestor/preceding elements
2082 function( elem, context, xml ) {
2083 var oldCache, outerCache,
2084 newCache = [ dirruns, doneName ];
2086 // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
2088 while ( (elem = elem[ dir ]) ) {
2089 if ( elem.nodeType === 1 || checkNonElements ) {
2090 if ( matcher( elem, context, xml ) ) {
2096 while ( (elem = elem[ dir ]) ) {
2097 if ( elem.nodeType === 1 || checkNonElements ) {
2098 outerCache = elem[ expando ] || (elem[ expando ] = {});
2099 if ( (oldCache = outerCache[ dir ]) &&
2100 oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
2102 // Assign to newCache so results back-propagate to previous elements
2103 return (newCache[ 2 ] = oldCache[ 2 ]);
2105 // Reuse newcache so results back-propagate to previous elements
2106 outerCache[ dir ] = newCache;
2108 // A match means we're done; a fail means we have to keep checking
2109 if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
2119 function elementMatcher( matchers ) {
2120 return matchers.length > 1 ?
2121 function( elem, context, xml ) {
2122 var i = matchers.length;
2124 if ( !matchers[i]( elem, context, xml ) ) {
2133 function multipleContexts( selector, contexts, results ) {
2135 len = contexts.length;
2136 for ( ; i < len; i++ ) {
2137 Sizzle( selector, contexts[i], results );
2142 function condense( unmatched, map, filter, context, xml ) {
2146 len = unmatched.length,
2147 mapped = map != null;
2149 for ( ; i < len; i++ ) {
2150 if ( (elem = unmatched[i]) ) {
2151 if ( !filter || filter( elem, context, xml ) ) {
2152 newUnmatched.push( elem );
2160 return newUnmatched;
2163 function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
2164 if ( postFilter && !postFilter[ expando ] ) {
2165 postFilter = setMatcher( postFilter );
2167 if ( postFinder && !postFinder[ expando ] ) {
2168 postFinder = setMatcher( postFinder, postSelector );
2170 return markFunction(function( seed, results, context, xml ) {
2174 preexisting = results.length,
2176 // Get initial elements from seed or context
2177 elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
2179 // Prefilter to get matcher input, preserving a map for seed-results synchronization
2180 matcherIn = preFilter && ( seed || !selector ) ?
2181 condense( elems, preMap, preFilter, context, xml ) :
2184 matcherOut = matcher ?
2185 // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
2186 postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
2188 // ...intermediate processing is necessary
2191 // ...otherwise use results directly
2195 // Find primary matches
2197 matcher( matcherIn, matcherOut, context, xml );
2202 temp = condense( matcherOut, postMap );
2203 postFilter( temp, [], context, xml );
2205 // Un-match failing elements by moving them back to matcherIn
2208 if ( (elem = temp[i]) ) {
2209 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
2215 if ( postFinder || preFilter ) {
2217 // Get the final matcherOut by condensing this intermediate into postFinder contexts
2219 i = matcherOut.length;
2221 if ( (elem = matcherOut[i]) ) {
2222 // Restore matcherIn since elem is not yet a final match
2223 temp.push( (matcherIn[i] = elem) );
2226 postFinder( null, (matcherOut = []), temp, xml );
2229 // Move matched elements from seed to results to keep them synchronized
2230 i = matcherOut.length;
2232 if ( (elem = matcherOut[i]) &&
2233 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
2235 seed[temp] = !(results[temp] = elem);
2240 // Add elements to results, through postFinder if defined
2242 matcherOut = condense(
2243 matcherOut === results ?
2244 matcherOut.splice( preexisting, matcherOut.length ) :
2248 postFinder( null, results, matcherOut, xml );
2250 push.apply( results, matcherOut );
2256 function matcherFromTokens( tokens ) {
2257 var checkContext, matcher, j,
2258 len = tokens.length,
2259 leadingRelative = Expr.relative[ tokens[0].type ],
2260 implicitRelative = leadingRelative || Expr.relative[" "],
2261 i = leadingRelative ? 1 : 0,
2263 // The foundational matcher ensures that elements are reachable from top-level context(s)
2264 matchContext = addCombinator( function( elem ) {
2265 return elem === checkContext;
2266 }, implicitRelative, true ),
2267 matchAnyContext = addCombinator( function( elem ) {
2268 return indexOf.call( checkContext, elem ) > -1;
2269 }, implicitRelative, true ),
2270 matchers = [ function( elem, context, xml ) {
2271 return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
2272 (checkContext = context).nodeType ?
2273 matchContext( elem, context, xml ) :
2274 matchAnyContext( elem, context, xml ) );
2277 for ( ; i < len; i++ ) {
2278 if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
2279 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
2281 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
2283 // Return special upon seeing a positional matcher
2284 if ( matcher[ expando ] ) {
2285 // Find the next relative operator (if any) for proper handling
2287 for ( ; j < len; j++ ) {
2288 if ( Expr.relative[ tokens[j].type ] ) {
2293 i > 1 && elementMatcher( matchers ),
2294 i > 1 && toSelector(
2295 // If the preceding token was a descendant combinator, insert an implicit any-element `*`
2296 tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
2297 ).replace( rtrim, "$1" ),
2299 i < j && matcherFromTokens( tokens.slice( i, j ) ),
2300 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
2301 j < len && toSelector( tokens )
2304 matchers.push( matcher );
2308 return elementMatcher( matchers );
2311 function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
2312 var bySet = setMatchers.length > 0,
2313 byElement = elementMatchers.length > 0,
2314 superMatcher = function( seed, context, xml, results, outermost ) {
2315 var elem, j, matcher,
2318 unmatched = seed && [],
2320 contextBackup = outermostContext,
2321 // We must always have either seed elements or outermost context
2322 elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
2323 // Use integer dirruns iff this is the outermost matcher
2324 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
2328 outermostContext = context !== document && context;
2331 // Add elements passing elementMatchers directly to results
2332 // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
2333 // Support: IE<9, Safari
2334 // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
2335 for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
2336 if ( byElement && elem ) {
2338 while ( (matcher = elementMatchers[j++]) ) {
2339 if ( matcher( elem, context, xml ) ) {
2340 results.push( elem );
2345 dirruns = dirrunsUnique;
2349 // Track unmatched elements for set filters
2351 // They will have gone through all possible matchers
2352 if ( (elem = !matcher && elem) ) {
2356 // Lengthen the array for every element, matched or not
2358 unmatched.push( elem );
2363 // Apply set filters to unmatched elements
2365 if ( bySet && i !== matchedCount ) {
2367 while ( (matcher = setMatchers[j++]) ) {
2368 matcher( unmatched, setMatched, context, xml );
2372 // Reintegrate element matches to eliminate the need for sorting
2373 if ( matchedCount > 0 ) {
2375 if ( !(unmatched[i] || setMatched[i]) ) {
2376 setMatched[i] = pop.call( results );
2381 // Discard index placeholder values to get only actual matches
2382 setMatched = condense( setMatched );
2385 // Add matches to results
2386 push.apply( results, setMatched );
2388 // Seedless set matches succeeding multiple successful matchers stipulate sorting
2389 if ( outermost && !seed && setMatched.length > 0 &&
2390 ( matchedCount + setMatchers.length ) > 1 ) {
2392 Sizzle.uniqueSort( results );
2396 // Override manipulation of globals by nested matchers
2398 dirruns = dirrunsUnique;
2399 outermostContext = contextBackup;
2406 markFunction( superMatcher ) :
2410 compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
2413 elementMatchers = [],
2414 cached = compilerCache[ selector + " " ];
2417 // Generate a function of recursive functions that can be used to check each element
2419 match = tokenize( selector );
2423 cached = matcherFromTokens( match[i] );
2424 if ( cached[ expando ] ) {
2425 setMatchers.push( cached );
2427 elementMatchers.push( cached );
2431 // Cache the compiled function
2432 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
2434 // Save selector and tokenization
2435 cached.selector = selector;
2441 * A low-level selection function that works with Sizzle's compiled
2442 * selector functions
2443 * @param {String|Function} selector A selector or a pre-compiled
2444 * selector function built with Sizzle.compile
2445 * @param {Element} context
2446 * @param {Array} [results]
2447 * @param {Array} [seed] A set of elements to match against
2449 select = Sizzle.select = function( selector, context, results, seed ) {
2450 var i, tokens, token, type, find,
2451 compiled = typeof selector === "function" && selector,
2452 match = !seed && tokenize( (selector = compiled.selector || selector) );
2454 results = results || [];
2456 // Try to minimize operations if there is no seed and only one group
2457 if ( match.length === 1 ) {
2459 // Take a shortcut and set the context if the root selector is an ID
2460 tokens = match[0] = match[0].slice( 0 );
2461 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
2462 support.getById && context.nodeType === 9 && documentIsHTML &&
2463 Expr.relative[ tokens[1].type ] ) {
2465 context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
2469 // Precompiled matchers will still verify ancestry, so step up a level
2470 } else if ( compiled ) {
2471 context = context.parentNode;
2474 selector = selector.slice( tokens.shift().value.length );
2477 // Fetch a seed set for right-to-left matching
2478 i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
2482 // Abort if we hit a combinator
2483 if ( Expr.relative[ (type = token.type) ] ) {
2486 if ( (find = Expr.find[ type ]) ) {
2487 // Search, expanding context for leading sibling combinators
2489 token.matches[0].replace( runescape, funescape ),
2490 rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
2493 // If seed is empty or no tokens remain, we can return early
2494 tokens.splice( i, 1 );
2495 selector = seed.length && toSelector( tokens );
2497 push.apply( results, seed );
2507 // Compile and execute a filtering function if one is not provided
2508 // Provide `match` to avoid retokenization if we modified the selector above
2509 ( compiled || compile( selector, match ) )(
2514 rsibling.test( selector ) && testContext( context.parentNode ) || context
2519 // One-time assignments
2522 support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
2524 // Support: Chrome<14
2525 // Always assume duplicates if they aren't passed to the comparison function
2526 support.detectDuplicates = !!hasDuplicate;
2528 // Initialize against the default document
2531 // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
2532 // Detached nodes confoundingly follow *each other*
2533 support.sortDetached = assert(function( div1 ) {
2534 // Should return 1, but returns 4 (following)
2535 return div1.compareDocumentPosition( document.createElement("div") ) & 1;
2539 // Prevent attribute/property "interpolation"
2540 // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
2541 if ( !assert(function( div ) {
2542 div.innerHTML = "<a href='#'></a>";
2543 return div.firstChild.getAttribute("href") === "#" ;
2545 addHandle( "type|href|height|width", function( elem, name, isXML ) {
2547 return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
2553 // Use defaultValue in place of getAttribute("value")
2554 if ( !support.attributes || !assert(function( div ) {
2555 div.innerHTML = "<input/>";
2556 div.firstChild.setAttribute( "value", "" );
2557 return div.firstChild.getAttribute( "value" ) === "";
2559 addHandle( "value", function( elem, name, isXML ) {
2560 if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
2561 return elem.defaultValue;
2567 // Use getAttributeNode to fetch booleans when getAttribute lies
2568 if ( !assert(function( div ) {
2569 return div.getAttribute("disabled") == null;
2571 addHandle( booleans, function( elem, name, isXML ) {
2574 return elem[ name ] === true ? name.toLowerCase() :
2575 (val = elem.getAttributeNode( name )) && val.specified ?
2588 jQuery.find = Sizzle;
2589 jQuery.expr = Sizzle.selectors;
2590 jQuery.expr[":"] = jQuery.expr.pseudos;
2591 jQuery.unique = Sizzle.uniqueSort;
2592 jQuery.text = Sizzle.getText;
2593 jQuery.isXMLDoc = Sizzle.isXML;
2594 jQuery.contains = Sizzle.contains;
2598 var rneedsContext = jQuery.expr.match.needsContext;
2600 var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
2604 var risSimple = /^.[^:#\[\.,]*$/;
2606 // Implement the identical functionality for filter and not
2607 function winnow( elements, qualifier, not ) {
2608 if ( jQuery.isFunction( qualifier ) ) {
2609 return jQuery.grep( elements, function( elem, i ) {
2611 return !!qualifier.call( elem, i, elem ) !== not;
2616 if ( qualifier.nodeType ) {
2617 return jQuery.grep( elements, function( elem ) {
2618 return ( elem === qualifier ) !== not;
2623 if ( typeof qualifier === "string" ) {
2624 if ( risSimple.test( qualifier ) ) {
2625 return jQuery.filter( qualifier, elements, not );
2628 qualifier = jQuery.filter( qualifier, elements );
2631 return jQuery.grep( elements, function( elem ) {
2632 return ( indexOf.call( qualifier, elem ) >= 0 ) !== not;
2636 jQuery.filter = function( expr, elems, not ) {
2637 var elem = elems[ 0 ];
2640 expr = ":not(" + expr + ")";
2643 return elems.length === 1 && elem.nodeType === 1 ?
2644 jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
2645 jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
2646 return elem.nodeType === 1;
2651 find: function( selector ) {
2657 if ( typeof selector !== "string" ) {
2658 return this.pushStack( jQuery( selector ).filter(function() {
2659 for ( i = 0; i < len; i++ ) {
2660 if ( jQuery.contains( self[ i ], this ) ) {
2667 for ( i = 0; i < len; i++ ) {
2668 jQuery.find( selector, self[ i ], ret );
2671 // Needed because $( selector, context ) becomes $( context ).find( selector )
2672 ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
2673 ret.selector = this.selector ? this.selector + " " + selector : selector;
2676 filter: function( selector ) {
2677 return this.pushStack( winnow(this, selector || [], false) );
2679 not: function( selector ) {
2680 return this.pushStack( winnow(this, selector || [], true) );
2682 is: function( selector ) {
2686 // If this is a positional/relative selector, check membership in the returned set
2687 // so $("p:first").is("p:last") won't return true for a doc with two "p".
2688 typeof selector === "string" && rneedsContext.test( selector ) ?
2689 jQuery( selector ) :
2697 // Initialize a jQuery object
2700 // A central reference to the root jQuery(document)
2703 // A simple way to check for HTML strings
2704 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
2705 // Strict HTML recognition (#11290: must start with <)
2706 rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
2708 init = jQuery.fn.init = function( selector, context ) {
2711 // HANDLE: $(""), $(null), $(undefined), $(false)
2716 // Handle HTML strings
2717 if ( typeof selector === "string" ) {
2718 if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
2719 // Assume that strings that start and end with <> are HTML and skip the regex check
2720 match = [ null, selector, null ];
2723 match = rquickExpr.exec( selector );
2726 // Match html or make sure no context is specified for #id
2727 if ( match && (match[1] || !context) ) {
2729 // HANDLE: $(html) -> $(array)
2731 context = context instanceof jQuery ? context[0] : context;
2733 // scripts is true for back-compat
2734 // Intentionally let the error be thrown if parseHTML is not present
2735 jQuery.merge( this, jQuery.parseHTML(
2737 context && context.nodeType ? context.ownerDocument || context : document,
2741 // HANDLE: $(html, props)
2742 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
2743 for ( match in context ) {
2744 // Properties of context are called as methods if possible
2745 if ( jQuery.isFunction( this[ match ] ) ) {
2746 this[ match ]( context[ match ] );
2748 // ...and otherwise set as attributes
2750 this.attr( match, context[ match ] );
2759 elem = document.getElementById( match[2] );
2761 // Check parentNode to catch when Blackberry 4.6 returns
2762 // nodes that are no longer in the document #6963
2763 if ( elem && elem.parentNode ) {
2764 // Inject the element directly into the jQuery object
2769 this.context = document;
2770 this.selector = selector;
2774 // HANDLE: $(expr, $(...))
2775 } else if ( !context || context.jquery ) {
2776 return ( context || rootjQuery ).find( selector );
2778 // HANDLE: $(expr, context)
2779 // (which is just equivalent to: $(context).find(expr)
2781 return this.constructor( context ).find( selector );
2784 // HANDLE: $(DOMElement)
2785 } else if ( selector.nodeType ) {
2786 this.context = this[0] = selector;
2790 // HANDLE: $(function)
2791 // Shortcut for document ready
2792 } else if ( jQuery.isFunction( selector ) ) {
2793 return typeof rootjQuery.ready !== "undefined" ?
2794 rootjQuery.ready( selector ) :
2795 // Execute immediately if ready is not present
2799 if ( selector.selector !== undefined ) {
2800 this.selector = selector.selector;
2801 this.context = selector.context;
2804 return jQuery.makeArray( selector, this );
2807 // Give the init function the jQuery prototype for later instantiation
2808 init.prototype = jQuery.fn;
2810 // Initialize central reference
2811 rootjQuery = jQuery( document );
2814 var rparentsprev = /^(?:parents|prev(?:Until|All))/,
2815 // methods guaranteed to produce a unique set when starting from a unique set
2816 guaranteedUnique = {
2824 dir: function( elem, dir, until ) {
2826 truncate = until !== undefined;
2828 while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
2829 if ( elem.nodeType === 1 ) {
2830 if ( truncate && jQuery( elem ).is( until ) ) {
2833 matched.push( elem );
2839 sibling: function( n, elem ) {
2842 for ( ; n; n = n.nextSibling ) {
2843 if ( n.nodeType === 1 && n !== elem ) {
2853 has: function( target ) {
2854 var targets = jQuery( target, this ),
2857 return this.filter(function() {
2859 for ( ; i < l; i++ ) {
2860 if ( jQuery.contains( this, targets[i] ) ) {
2867 closest: function( selectors, context ) {
2872 pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
2873 jQuery( selectors, context || this.context ) :
2876 for ( ; i < l; i++ ) {
2877 for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
2878 // Always skip document fragments
2879 if ( cur.nodeType < 11 && (pos ?
2880 pos.index(cur) > -1 :
2882 // Don't pass non-elements to Sizzle
2883 cur.nodeType === 1 &&
2884 jQuery.find.matchesSelector(cur, selectors)) ) {
2886 matched.push( cur );
2892 return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
2895 // Determine the position of an element within
2896 // the matched set of elements
2897 index: function( elem ) {
2899 // No argument, return index in parent
2901 return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
2904 // index in selector
2905 if ( typeof elem === "string" ) {
2906 return indexOf.call( jQuery( elem ), this[ 0 ] );
2909 // Locate the position of the desired element
2910 return indexOf.call( this,
2912 // If it receives a jQuery object, the first element is used
2913 elem.jquery ? elem[ 0 ] : elem
2917 add: function( selector, context ) {
2918 return this.pushStack(
2920 jQuery.merge( this.get(), jQuery( selector, context ) )
2925 addBack: function( selector ) {
2926 return this.add( selector == null ?
2927 this.prevObject : this.prevObject.filter(selector)
2932 function sibling( cur, dir ) {
2933 while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
2938 parent: function( elem ) {
2939 var parent = elem.parentNode;
2940 return parent && parent.nodeType !== 11 ? parent : null;
2942 parents: function( elem ) {
2943 return jQuery.dir( elem, "parentNode" );
2945 parentsUntil: function( elem, i, until ) {
2946 return jQuery.dir( elem, "parentNode", until );
2948 next: function( elem ) {
2949 return sibling( elem, "nextSibling" );
2951 prev: function( elem ) {
2952 return sibling( elem, "previousSibling" );
2954 nextAll: function( elem ) {
2955 return jQuery.dir( elem, "nextSibling" );
2957 prevAll: function( elem ) {
2958 return jQuery.dir( elem, "previousSibling" );
2960 nextUntil: function( elem, i, until ) {
2961 return jQuery.dir( elem, "nextSibling", until );
2963 prevUntil: function( elem, i, until ) {
2964 return jQuery.dir( elem, "previousSibling", until );
2966 siblings: function( elem ) {
2967 return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
2969 children: function( elem ) {
2970 return jQuery.sibling( elem.firstChild );
2972 contents: function( elem ) {
2973 return elem.contentDocument || jQuery.merge( [], elem.childNodes );
2975 }, function( name, fn ) {
2976 jQuery.fn[ name ] = function( until, selector ) {
2977 var matched = jQuery.map( this, fn, until );
2979 if ( name.slice( -5 ) !== "Until" ) {
2983 if ( selector && typeof selector === "string" ) {
2984 matched = jQuery.filter( selector, matched );
2987 if ( this.length > 1 ) {
2988 // Remove duplicates
2989 if ( !guaranteedUnique[ name ] ) {
2990 jQuery.unique( matched );
2993 // Reverse order for parents* and prev-derivatives
2994 if ( rparentsprev.test( name ) ) {
2999 return this.pushStack( matched );
3002 var rnotwhite = (/\S+/g);
3006 // String to Object options format cache
3007 var optionsCache = {};
3009 // Convert String-formatted options into Object-formatted ones and store in cache
3010 function createOptions( options ) {
3011 var object = optionsCache[ options ] = {};
3012 jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
3013 object[ flag ] = true;
3019 * Create a callback list using the following parameters:
3021 * options: an optional list of space-separated options that will change how
3022 * the callback list behaves or a more traditional option object
3024 * By default a callback list will act like an event callback list and can be
3025 * "fired" multiple times.
3029 * once: will ensure the callback list can only be fired once (like a Deferred)
3031 * memory: will keep track of previous values and will call any callback added
3032 * after the list has been fired right away with the latest "memorized"
3033 * values (like a Deferred)
3035 * unique: will ensure a callback can only be added once (no duplicate in the list)
3037 * stopOnFalse: interrupt callings when a callback returns false
3040 jQuery.Callbacks = function( options ) {
3042 // Convert options from String-formatted to Object-formatted if needed
3043 // (we check in cache first)
3044 options = typeof options === "string" ?
3045 ( optionsCache[ options ] || createOptions( options ) ) :
3046 jQuery.extend( {}, options );
3048 var // Last fire value (for non-forgettable lists)
3050 // Flag to know if list was already fired
3052 // Flag to know if list is currently firing
3054 // First callback to fire (used internally by add and fireWith)
3056 // End of the loop when firing
3058 // Index of currently firing callback (modified by remove if needed)
3060 // Actual callback list
3062 // Stack of fire calls for repeatable lists
3063 stack = !options.once && [],
3065 fire = function( data ) {
3066 memory = options.memory && data;
3068 firingIndex = firingStart || 0;
3070 firingLength = list.length;
3072 for ( ; list && firingIndex < firingLength; firingIndex++ ) {
3073 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
3074 memory = false; // To prevent further calls using add
3081 if ( stack.length ) {
3082 fire( stack.shift() );
3084 } else if ( memory ) {
3091 // Actual Callbacks object
3093 // Add a callback or a collection of callbacks to the list
3096 // First, we save the current length
3097 var start = list.length;
3098 (function add( args ) {
3099 jQuery.each( args, function( _, arg ) {
3100 var type = jQuery.type( arg );
3101 if ( type === "function" ) {
3102 if ( !options.unique || !self.has( arg ) ) {
3105 } else if ( arg && arg.length && type !== "string" ) {
3106 // Inspect recursively
3111 // Do we need to add the callbacks to the
3112 // current firing batch?
3114 firingLength = list.length;
3115 // With memory, if we're not firing then
3116 // we should call right away
3117 } else if ( memory ) {
3118 firingStart = start;
3124 // Remove a callback from the list
3125 remove: function() {
3127 jQuery.each( arguments, function( _, arg ) {
3129 while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
3130 list.splice( index, 1 );
3131 // Handle firing indexes
3133 if ( index <= firingLength ) {
3136 if ( index <= firingIndex ) {
3145 // Check if a given callback is in the list.
3146 // If no argument is given, return whether or not list has callbacks attached.
3147 has: function( fn ) {
3148 return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
3150 // Remove all callbacks from the list
3156 // Have the list do nothing anymore
3157 disable: function() {
3158 list = stack = memory = undefined;
3162 disabled: function() {
3165 // Lock the list in its current state
3174 locked: function() {
3177 // Call all callbacks with the given context and arguments
3178 fireWith: function( context, args ) {
3179 if ( list && ( !fired || stack ) ) {
3181 args = [ context, args.slice ? args.slice() : args ];
3190 // Call all the callbacks with the given arguments
3192 self.fireWith( this, arguments );
3195 // To know if the callbacks have already been called at least once
3207 Deferred: function( func ) {
3209 // action, add listener, listener list, final state
3210 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
3211 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
3212 [ "notify", "progress", jQuery.Callbacks("memory") ]
3219 always: function() {
3220 deferred.done( arguments ).fail( arguments );
3223 then: function( /* fnDone, fnFail, fnProgress */ ) {
3224 var fns = arguments;
3225 return jQuery.Deferred(function( newDefer ) {
3226 jQuery.each( tuples, function( i, tuple ) {
3227 var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
3228 // deferred[ done | fail | progress ] for forwarding actions to newDefer
3229 deferred[ tuple[1] ](function() {
3230 var returned = fn && fn.apply( this, arguments );
3231 if ( returned && jQuery.isFunction( returned.promise ) ) {
3233 .done( newDefer.resolve )
3234 .fail( newDefer.reject )
3235 .progress( newDefer.notify );
3237 newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
3244 // Get a promise for this deferred
3245 // If obj is provided, the promise aspect is added to the object
3246 promise: function( obj ) {
3247 return obj != null ? jQuery.extend( obj, promise ) : promise;
3252 // Keep pipe for back-compat
3253 promise.pipe = promise.then;
3255 // Add list-specific methods
3256 jQuery.each( tuples, function( i, tuple ) {
3257 var list = tuple[ 2 ],
3258 stateString = tuple[ 3 ];
3260 // promise[ done | fail | progress ] = list.add
3261 promise[ tuple[1] ] = list.add;
3264 if ( stateString ) {
3265 list.add(function() {
3266 // state = [ resolved | rejected ]
3267 state = stateString;
3269 // [ reject_list | resolve_list ].disable; progress_list.lock
3270 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
3273 // deferred[ resolve | reject | notify ]
3274 deferred[ tuple[0] ] = function() {
3275 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
3278 deferred[ tuple[0] + "With" ] = list.fireWith;
3281 // Make the deferred a promise
3282 promise.promise( deferred );
3284 // Call given func if any
3286 func.call( deferred, deferred );
3294 when: function( subordinate /* , ..., subordinateN */ ) {
3296 resolveValues = slice.call( arguments ),
3297 length = resolveValues.length,
3299 // the count of uncompleted subordinates
3300 remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
3302 // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
3303 deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
3305 // Update function for both resolve and progress values
3306 updateFunc = function( i, contexts, values ) {
3307 return function( value ) {
3308 contexts[ i ] = this;
3309 values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
3310 if ( values === progressValues ) {
3311 deferred.notifyWith( contexts, values );
3312 } else if ( !( --remaining ) ) {
3313 deferred.resolveWith( contexts, values );
3318 progressValues, progressContexts, resolveContexts;
3320 // add listeners to Deferred subordinates; treat others as resolved
3322 progressValues = new Array( length );
3323 progressContexts = new Array( length );
3324 resolveContexts = new Array( length );
3325 for ( ; i < length; i++ ) {
3326 if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
3327 resolveValues[ i ].promise()
3328 .done( updateFunc( i, resolveContexts, resolveValues ) )
3329 .fail( deferred.reject )
3330 .progress( updateFunc( i, progressContexts, progressValues ) );
3337 // if we're not waiting on anything, resolve the master
3339 deferred.resolveWith( resolveContexts, resolveValues );
3342 return deferred.promise();
3347 // The deferred used on DOM ready
3350 jQuery.fn.ready = function( fn ) {
3352 jQuery.ready.promise().done( fn );
3358 // Is the DOM ready to be used? Set to true once it occurs.
3361 // A counter to track how many items to wait for before
3362 // the ready event fires. See #6781
3365 // Hold (or release) the ready event
3366 holdReady: function( hold ) {
3370 jQuery.ready( true );
3374 // Handle when the DOM is ready
3375 ready: function( wait ) {
3377 // Abort if there are pending holds or we're already ready
3378 if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
3382 // Remember that the DOM is ready
3383 jQuery.isReady = true;
3385 // If a normal DOM Ready event fired, decrement, and wait if need be
3386 if ( wait !== true && --jQuery.readyWait > 0 ) {
3390 // If there are functions bound, to execute
3391 readyList.resolveWith( document, [ jQuery ] );
3393 // Trigger any bound ready events
3394 if ( jQuery.fn.triggerHandler ) {
3395 jQuery( document ).triggerHandler( "ready" );
3396 jQuery( document ).off( "ready" );
3402 * The ready event handler and self cleanup method
3404 function completed() {
3405 document.removeEventListener( "DOMContentLoaded", completed, false );
3406 window.removeEventListener( "load", completed, false );
3410 jQuery.ready.promise = function( obj ) {
3413 readyList = jQuery.Deferred();
3415 // Catch cases where $(document).ready() is called after the browser event has already occurred.
3416 // we once tried to use readyState "interactive" here, but it caused issues like the one
3417 // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
3418 if ( document.readyState === "complete" ) {
3419 // Handle it asynchronously to allow scripts the opportunity to delay ready
3420 setTimeout( jQuery.ready );
3424 // Use the handy event callback
3425 document.addEventListener( "DOMContentLoaded", completed, false );
3427 // A fallback to window.onload, that will always work
3428 window.addEventListener( "load", completed, false );
3431 return readyList.promise( obj );
3434 // Kick off the DOM ready check even if the user does not
3435 jQuery.ready.promise();
3440 // Multifunctional method to get and set values of a collection
3441 // The value/s can optionally be executed if it's a function
3442 var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
3448 if ( jQuery.type( key ) === "object" ) {
3451 jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
3455 } else if ( value !== undefined ) {
3458 if ( !jQuery.isFunction( value ) ) {
3463 // Bulk operations run against the entire set
3465 fn.call( elems, value );
3468 // ...except when executing function values
3471 fn = function( elem, key, value ) {
3472 return bulk.call( jQuery( elem ), value );
3478 for ( ; i < len; i++ ) {
3479 fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
3490 len ? fn( elems[0], key ) : emptyGet;
3495 * Determines whether an object can have data
3497 jQuery.acceptData = function( owner ) {
3500 // - Node.ELEMENT_NODE
3501 // - Node.DOCUMENT_NODE
3505 return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
3510 // Support: Android < 4,
3511 // Old WebKit does not have Object.preventExtensions/freeze method,
3512 // return new empty object instead with no [[set]] accessor
3513 Object.defineProperty( this.cache = {}, 0, {
3519 this.expando = jQuery.expando + Math.random();
3523 Data.accepts = jQuery.acceptData;
3526 key: function( owner ) {
3527 // We can accept data for non-element nodes in modern browsers,
3528 // but we should not, see #8335.
3529 // Always return the key for a frozen object.
3530 if ( !Data.accepts( owner ) ) {
3534 var descriptor = {},
3535 // Check if the owner object already has a cache key
3536 unlock = owner[ this.expando ];
3538 // If not, create one
3540 unlock = Data.uid++;
3542 // Secure it in a non-enumerable, non-writable property
3544 descriptor[ this.expando ] = { value: unlock };
3545 Object.defineProperties( owner, descriptor );
3547 // Support: Android < 4
3548 // Fallback to a less secure definition
3550 descriptor[ this.expando ] = unlock;
3551 jQuery.extend( owner, descriptor );
3555 // Ensure the cache object
3556 if ( !this.cache[ unlock ] ) {
3557 this.cache[ unlock ] = {};
3562 set: function( owner, data, value ) {
3564 // There may be an unlock assigned to this node,
3565 // if there is no entry for this "owner", create one inline
3566 // and set the unlock as though an owner entry had always existed
3567 unlock = this.key( owner ),
3568 cache = this.cache[ unlock ];
3570 // Handle: [ owner, key, value ] args
3571 if ( typeof data === "string" ) {
3572 cache[ data ] = value;
3574 // Handle: [ owner, { properties } ] args
3576 // Fresh assignments by object are shallow copied
3577 if ( jQuery.isEmptyObject( cache ) ) {
3578 jQuery.extend( this.cache[ unlock ], data );
3579 // Otherwise, copy the properties one-by-one to the cache object
3581 for ( prop in data ) {
3582 cache[ prop ] = data[ prop ];
3588 get: function( owner, key ) {
3589 // Either a valid cache is found, or will be created.
3590 // New caches will be created and the unlock returned,
3591 // allowing direct access to the newly created
3592 // empty data object. A valid owner object must be provided.
3593 var cache = this.cache[ this.key( owner ) ];
3595 return key === undefined ?
3596 cache : cache[ key ];
3598 access: function( owner, key, value ) {
3600 // In cases where either:
3602 // 1. No key was specified
3603 // 2. A string key was specified, but no value provided
3605 // Take the "read" path and allow the get method to determine
3606 // which value to return, respectively either:
3608 // 1. The entire cache object
3609 // 2. The data stored at the key
3611 if ( key === undefined ||
3612 ((key && typeof key === "string") && value === undefined) ) {
3614 stored = this.get( owner, key );
3616 return stored !== undefined ?
3617 stored : this.get( owner, jQuery.camelCase(key) );
3620 // [*]When the key is not a string, or both a key and value
3621 // are specified, set or extend (existing objects) with either:
3623 // 1. An object of properties
3624 // 2. A key and value
3626 this.set( owner, key, value );
3628 // Since the "set" path can have two possible entry points
3629 // return the expected data based on which path was taken[*]
3630 return value !== undefined ? value : key;
3632 remove: function( owner, key ) {
3634 unlock = this.key( owner ),
3635 cache = this.cache[ unlock ];
3637 if ( key === undefined ) {
3638 this.cache[ unlock ] = {};
3641 // Support array or space separated string of keys
3642 if ( jQuery.isArray( key ) ) {
3643 // If "name" is an array of keys...
3644 // When data is initially created, via ("key", "val") signature,
3645 // keys will be converted to camelCase.
3646 // Since there is no way to tell _how_ a key was added, remove
3647 // both plain key and camelCase key. #12786
3648 // This will only penalize the array argument path.
3649 name = key.concat( key.map( jQuery.camelCase ) );
3651 camel = jQuery.camelCase( key );
3652 // Try the string as a key before any manipulation
3653 if ( key in cache ) {
3654 name = [ key, camel ];
3656 // If a key with the spaces exists, use it.
3657 // Otherwise, create an array by matching non-whitespace
3659 name = name in cache ?
3660 [ name ] : ( name.match( rnotwhite ) || [] );
3666 delete cache[ name[ i ] ];
3670 hasData: function( owner ) {
3671 return !jQuery.isEmptyObject(
3672 this.cache[ owner[ this.expando ] ] || {}
3675 discard: function( owner ) {
3676 if ( owner[ this.expando ] ) {
3677 delete this.cache[ owner[ this.expando ] ];
3681 var data_priv = new Data();
3683 var data_user = new Data();
3688 Implementation Summary
3690 1. Enforce API surface and semantic compatibility with 1.9.x branch
3691 2. Improve the module's maintainability by reducing the storage
3692 paths to a single mechanism.
3693 3. Use the same single mechanism to support "private" and "user" data.
3694 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
3695 5. Avoid exposing implementation details on user objects (eg. expando properties)
3696 6. Provide a clear path for implementation upgrade to WeakMap in 2014
3698 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
3699 rmultiDash = /([A-Z])/g;
3701 function dataAttr( elem, key, data ) {
3704 // If nothing was found internally, try to fetch any
3705 // data from the HTML5 data-* attribute
3706 if ( data === undefined && elem.nodeType === 1 ) {
3707 name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
3708 data = elem.getAttribute( name );
3710 if ( typeof data === "string" ) {
3712 data = data === "true" ? true :
3713 data === "false" ? false :
3714 data === "null" ? null :
3715 // Only convert to a number if it doesn't change the string
3716 +data + "" === data ? +data :
3717 rbrace.test( data ) ? jQuery.parseJSON( data ) :
3721 // Make sure we set the data so it isn't changed later
3722 data_user.set( elem, key, data );
3731 hasData: function( elem ) {
3732 return data_user.hasData( elem ) || data_priv.hasData( elem );
3735 data: function( elem, name, data ) {
3736 return data_user.access( elem, name, data );
3739 removeData: function( elem, name ) {
3740 data_user.remove( elem, name );
3743 // TODO: Now that all calls to _data and _removeData have been replaced
3744 // with direct calls to data_priv methods, these can be deprecated.
3745 _data: function( elem, name, data ) {
3746 return data_priv.access( elem, name, data );
3749 _removeData: function( elem, name ) {
3750 data_priv.remove( elem, name );
3755 data: function( key, value ) {
3758 attrs = elem && elem.attributes;
3761 if ( key === undefined ) {
3762 if ( this.length ) {
3763 data = data_user.get( elem );
3765 if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
3770 // The attrs elements can be null (#14894)
3772 name = attrs[ i ].name;
3773 if ( name.indexOf( "data-" ) === 0 ) {
3774 name = jQuery.camelCase( name.slice(5) );
3775 dataAttr( elem, name, data[ name ] );
3779 data_priv.set( elem, "hasDataAttrs", true );
3786 // Sets multiple values
3787 if ( typeof key === "object" ) {
3788 return this.each(function() {
3789 data_user.set( this, key );
3793 return access( this, function( value ) {
3795 camelKey = jQuery.camelCase( key );
3797 // The calling jQuery object (element matches) is not empty
3798 // (and therefore has an element appears at this[ 0 ]) and the
3799 // `value` parameter was not undefined. An empty jQuery object
3800 // will result in `undefined` for elem = this[ 0 ] which will
3801 // throw an exception if an attempt to read a data cache is made.
3802 if ( elem && value === undefined ) {
3803 // Attempt to get data from the cache
3804 // with the key as-is
3805 data = data_user.get( elem, key );
3806 if ( data !== undefined ) {
3810 // Attempt to get data from the cache
3811 // with the key camelized
3812 data = data_user.get( elem, camelKey );
3813 if ( data !== undefined ) {
3817 // Attempt to "discover" the data in
3818 // HTML5 custom data-* attrs
3819 data = dataAttr( elem, camelKey, undefined );
3820 if ( data !== undefined ) {
3824 // We tried really hard, but the data doesn't exist.
3829 this.each(function() {
3830 // First, attempt to store a copy or reference of any
3831 // data that might've been store with a camelCased key.
3832 var data = data_user.get( this, camelKey );
3834 // For HTML5 data-* attribute interop, we have to
3835 // store property names with dashes in a camelCase form.
3836 // This might not apply to all properties...*
3837 data_user.set( this, camelKey, value );
3839 // *... In the case of properties that might _actually_
3840 // have dashes, we need to also store a copy of that
3841 // unchanged property.
3842 if ( key.indexOf("-") !== -1 && data !== undefined ) {
3843 data_user.set( this, key, value );
3846 }, null, value, arguments.length > 1, null, true );
3849 removeData: function( key ) {
3850 return this.each(function() {
3851 data_user.remove( this, key );
3858 queue: function( elem, type, data ) {
3862 type = ( type || "fx" ) + "queue";
3863 queue = data_priv.get( elem, type );
3865 // Speed up dequeue by getting out quickly if this is just a lookup
3867 if ( !queue || jQuery.isArray( data ) ) {
3868 queue = data_priv.access( elem, type, jQuery.makeArray(data) );
3877 dequeue: function( elem, type ) {
3878 type = type || "fx";
3880 var queue = jQuery.queue( elem, type ),
3881 startLength = queue.length,
3883 hooks = jQuery._queueHooks( elem, type ),
3885 jQuery.dequeue( elem, type );
3888 // If the fx queue is dequeued, always remove the progress sentinel
3889 if ( fn === "inprogress" ) {
3896 // Add a progress sentinel to prevent the fx queue from being
3897 // automatically dequeued
3898 if ( type === "fx" ) {
3899 queue.unshift( "inprogress" );
3902 // clear up the last queue stop function
3904 fn.call( elem, next, hooks );
3907 if ( !startLength && hooks ) {
3912 // not intended for public consumption - generates a queueHooks object, or returns the current one
3913 _queueHooks: function( elem, type ) {
3914 var key = type + "queueHooks";
3915 return data_priv.get( elem, key ) || data_priv.access( elem, key, {
3916 empty: jQuery.Callbacks("once memory").add(function() {
3917 data_priv.remove( elem, [ type + "queue", key ] );
3924 queue: function( type, data ) {
3927 if ( typeof type !== "string" ) {
3933 if ( arguments.length < setter ) {
3934 return jQuery.queue( this[0], type );
3937 return data === undefined ?
3939 this.each(function() {
3940 var queue = jQuery.queue( this, type, data );
3942 // ensure a hooks for this queue
3943 jQuery._queueHooks( this, type );
3945 if ( type === "fx" && queue[0] !== "inprogress" ) {
3946 jQuery.dequeue( this, type );
3950 dequeue: function( type ) {
3951 return this.each(function() {
3952 jQuery.dequeue( this, type );
3955 clearQueue: function( type ) {
3956 return this.queue( type || "fx", [] );
3958 // Get a promise resolved when queues of a certain type
3959 // are emptied (fx is the type by default)
3960 promise: function( type, obj ) {
3963 defer = jQuery.Deferred(),
3966 resolve = function() {
3967 if ( !( --count ) ) {
3968 defer.resolveWith( elements, [ elements ] );
3972 if ( typeof type !== "string" ) {
3976 type = type || "fx";
3979 tmp = data_priv.get( elements[ i ], type + "queueHooks" );
3980 if ( tmp && tmp.empty ) {
3982 tmp.empty.add( resolve );
3986 return defer.promise( obj );
3989 var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
3991 var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
3993 var isHidden = function( elem, el ) {
3994 // isHidden might be called from jQuery#filter function;
3995 // in that case, element will be second argument
3997 return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
4000 var rcheckableType = (/^(?:checkbox|radio)$/i);
4005 var fragment = document.createDocumentFragment(),
4006 div = fragment.appendChild( document.createElement( "div" ) ),
4007 input = document.createElement( "input" );
4009 // #11217 - WebKit loses check when the name is after the checked attribute
4010 // Support: Windows Web Apps (WWA)
4011 // `name` and `type` need .setAttribute for WWA
4012 input.setAttribute( "type", "radio" );
4013 input.setAttribute( "checked", "checked" );
4014 input.setAttribute( "name", "t" );
4016 div.appendChild( input );
4018 // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
4019 // old WebKit doesn't clone checked state correctly in fragments
4020 support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
4022 // Make sure textarea (and checkbox) defaultValue is properly cloned
4023 // Support: IE9-IE11+
4024 div.innerHTML = "<textarea>x</textarea>";
4025 support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
4027 var strundefined = typeof undefined;
4031 support.focusinBubbles = "onfocusin" in window;
4036 rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
4037 rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
4038 rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
4040 function returnTrue() {
4044 function returnFalse() {
4048 function safeActiveElement() {
4050 return document.activeElement;
4055 * Helper functions for managing events -- not part of the public interface.
4056 * Props to Dean Edwards' addEvent library for many of the ideas.
4062 add: function( elem, types, handler, data, selector ) {
4064 var handleObjIn, eventHandle, tmp,
4065 events, t, handleObj,
4066 special, handlers, type, namespaces, origType,
4067 elemData = data_priv.get( elem );
4069 // Don't attach events to noData or text/comment nodes (but allow plain objects)
4074 // Caller can pass in an object of custom data in lieu of the handler
4075 if ( handler.handler ) {
4076 handleObjIn = handler;
4077 handler = handleObjIn.handler;
4078 selector = handleObjIn.selector;
4081 // Make sure that the handler has a unique ID, used to find/remove it later
4082 if ( !handler.guid ) {
4083 handler.guid = jQuery.guid++;
4086 // Init the element's event structure and main handler, if this is the first
4087 if ( !(events = elemData.events) ) {
4088 events = elemData.events = {};
4090 if ( !(eventHandle = elemData.handle) ) {
4091 eventHandle = elemData.handle = function( e ) {
4092 // Discard the second event of a jQuery.event.trigger() and
4093 // when an event is called after a page has unloaded
4094 return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
4095 jQuery.event.dispatch.apply( elem, arguments ) : undefined;
4099 // Handle multiple events separated by a space
4100 types = ( types || "" ).match( rnotwhite ) || [ "" ];
4103 tmp = rtypenamespace.exec( types[t] ) || [];
4104 type = origType = tmp[1];
4105 namespaces = ( tmp[2] || "" ).split( "." ).sort();
4107 // There *must* be a type, no attaching namespace-only handlers
4112 // If event changes its type, use the special event handlers for the changed type
4113 special = jQuery.event.special[ type ] || {};
4115 // If selector defined, determine special event api type, otherwise given type
4116 type = ( selector ? special.delegateType : special.bindType ) || type;
4118 // Update special based on newly reset type
4119 special = jQuery.event.special[ type ] || {};
4121 // handleObj is passed to all event handlers
4122 handleObj = jQuery.extend({
4129 needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
4130 namespace: namespaces.join(".")
4133 // Init the event handler queue if we're the first
4134 if ( !(handlers = events[ type ]) ) {
4135 handlers = events[ type ] = [];
4136 handlers.delegateCount = 0;
4138 // Only use addEventListener if the special events handler returns false
4139 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
4140 if ( elem.addEventListener ) {
4141 elem.addEventListener( type, eventHandle, false );
4146 if ( special.add ) {
4147 special.add.call( elem, handleObj );
4149 if ( !handleObj.handler.guid ) {
4150 handleObj.handler.guid = handler.guid;
4154 // Add to the element's handler list, delegates in front
4156 handlers.splice( handlers.delegateCount++, 0, handleObj );
4158 handlers.push( handleObj );
4161 // Keep track of which events have ever been used, for event optimization
4162 jQuery.event.global[ type ] = true;
4167 // Detach an event or set of events from an element
4168 remove: function( elem, types, handler, selector, mappedTypes ) {
4170 var j, origCount, tmp,
4171 events, t, handleObj,
4172 special, handlers, type, namespaces, origType,
4173 elemData = data_priv.hasData( elem ) && data_priv.get( elem );
4175 if ( !elemData || !(events = elemData.events) ) {
4179 // Once for each type.namespace in types; type may be omitted
4180 types = ( types || "" ).match( rnotwhite ) || [ "" ];
4183 tmp = rtypenamespace.exec( types[t] ) || [];
4184 type = origType = tmp[1];
4185 namespaces = ( tmp[2] || "" ).split( "." ).sort();
4187 // Unbind all events (on this namespace, if provided) for the element
4189 for ( type in events ) {
4190 jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
4195 special = jQuery.event.special[ type ] || {};
4196 type = ( selector ? special.delegateType : special.bindType ) || type;
4197 handlers = events[ type ] || [];
4198 tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
4200 // Remove matching events
4201 origCount = j = handlers.length;
4203 handleObj = handlers[ j ];
4205 if ( ( mappedTypes || origType === handleObj.origType ) &&
4206 ( !handler || handler.guid === handleObj.guid ) &&
4207 ( !tmp || tmp.test( handleObj.namespace ) ) &&
4208 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
4209 handlers.splice( j, 1 );
4211 if ( handleObj.selector ) {
4212 handlers.delegateCount--;
4214 if ( special.remove ) {
4215 special.remove.call( elem, handleObj );
4220 // Remove generic event handler if we removed something and no more handlers exist
4221 // (avoids potential for endless recursion during removal of special event handlers)
4222 if ( origCount && !handlers.length ) {
4223 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
4224 jQuery.removeEvent( elem, type, elemData.handle );
4227 delete events[ type ];
4231 // Remove the expando if it's no longer used
4232 if ( jQuery.isEmptyObject( events ) ) {
4233 delete elemData.handle;
4234 data_priv.remove( elem, "events" );
4238 trigger: function( event, data, elem, onlyHandlers ) {
4240 var i, cur, tmp, bubbleType, ontype, handle, special,
4241 eventPath = [ elem || document ],
4242 type = hasOwn.call( event, "type" ) ? event.type : event,
4243 namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
4245 cur = tmp = elem = elem || document;
4247 // Don't do events on text and comment nodes
4248 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
4252 // focus/blur morphs to focusin/out; ensure we're not firing them right now
4253 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
4257 if ( type.indexOf(".") >= 0 ) {
4258 // Namespaced trigger; create a regexp to match event type in handle()
4259 namespaces = type.split(".");
4260 type = namespaces.shift();
4263 ontype = type.indexOf(":") < 0 && "on" + type;
4265 // Caller can pass in a jQuery.Event object, Object, or just an event type string
4266 event = event[ jQuery.expando ] ?
4268 new jQuery.Event( type, typeof event === "object" && event );
4270 // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
4271 event.isTrigger = onlyHandlers ? 2 : 3;
4272 event.namespace = namespaces.join(".");
4273 event.namespace_re = event.namespace ?
4274 new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
4277 // Clean up the event in case it is being reused
4278 event.result = undefined;
4279 if ( !event.target ) {
4280 event.target = elem;
4283 // Clone any incoming data and prepend the event, creating the handler arg list
4284 data = data == null ?
4286 jQuery.makeArray( data, [ event ] );
4288 // Allow special events to draw outside the lines
4289 special = jQuery.event.special[ type ] || {};
4290 if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
4294 // Determine event propagation path in advance, per W3C events spec (#9951)
4295 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
4296 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
4298 bubbleType = special.delegateType || type;
4299 if ( !rfocusMorph.test( bubbleType + type ) ) {
4300 cur = cur.parentNode;
4302 for ( ; cur; cur = cur.parentNode ) {
4303 eventPath.push( cur );
4307 // Only add window if we got to document (e.g., not plain obj or detached DOM)
4308 if ( tmp === (elem.ownerDocument || document) ) {
4309 eventPath.push( tmp.defaultView || tmp.parentWindow || window );
4313 // Fire handlers on the event path
4315 while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
4317 event.type = i > 1 ?
4319 special.bindType || type;
4322 handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
4324 handle.apply( cur, data );
4328 handle = ontype && cur[ ontype ];
4329 if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
4330 event.result = handle.apply( cur, data );
4331 if ( event.result === false ) {
4332 event.preventDefault();
4338 // If nobody prevented the default action, do it now
4339 if ( !onlyHandlers && !event.isDefaultPrevented() ) {
4341 if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
4342 jQuery.acceptData( elem ) ) {
4344 // Call a native DOM method on the target with the same name name as the event.
4345 // Don't do default actions on window, that's where global variables be (#6170)
4346 if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
4348 // Don't re-trigger an onFOO event when we call its FOO() method
4349 tmp = elem[ ontype ];
4352 elem[ ontype ] = null;
4355 // Prevent re-triggering of the same event, since we already bubbled it above
4356 jQuery.event.triggered = type;
4358 jQuery.event.triggered = undefined;
4361 elem[ ontype ] = tmp;
4367 return event.result;
4370 dispatch: function( event ) {
4372 // Make a writable jQuery.Event from the native event object
4373 event = jQuery.event.fix( event );
4375 var i, j, ret, matched, handleObj,
4377 args = slice.call( arguments ),
4378 handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
4379 special = jQuery.event.special[ event.type ] || {};
4381 // Use the fix-ed jQuery.Event rather than the (read-only) native event
4383 event.delegateTarget = this;
4385 // Call the preDispatch hook for the mapped type, and let it bail if desired
4386 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
4390 // Determine handlers
4391 handlerQueue = jQuery.event.handlers.call( this, event, handlers );
4393 // Run delegates first; they may want to stop propagation beneath us
4395 while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
4396 event.currentTarget = matched.elem;
4399 while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
4401 // Triggered event must either 1) have no namespace, or
4402 // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
4403 if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
4405 event.handleObj = handleObj;
4406 event.data = handleObj.data;
4408 ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
4409 .apply( matched.elem, args );
4411 if ( ret !== undefined ) {
4412 if ( (event.result = ret) === false ) {
4413 event.preventDefault();
4414 event.stopPropagation();
4421 // Call the postDispatch hook for the mapped type
4422 if ( special.postDispatch ) {
4423 special.postDispatch.call( this, event );
4426 return event.result;
4429 handlers: function( event, handlers ) {
4430 var i, matches, sel, handleObj,
4432 delegateCount = handlers.delegateCount,
4435 // Find delegate handlers
4436 // Black-hole SVG <use> instance trees (#13180)
4437 // Avoid non-left-click bubbling in Firefox (#3861)
4438 if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
4440 for ( ; cur !== this; cur = cur.parentNode || this ) {
4442 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
4443 if ( cur.disabled !== true || event.type !== "click" ) {
4445 for ( i = 0; i < delegateCount; i++ ) {
4446 handleObj = handlers[ i ];
4448 // Don't conflict with Object.prototype properties (#13203)
4449 sel = handleObj.selector + " ";
4451 if ( matches[ sel ] === undefined ) {
4452 matches[ sel ] = handleObj.needsContext ?
4453 jQuery( sel, this ).index( cur ) >= 0 :
4454 jQuery.find( sel, this, null, [ cur ] ).length;
4456 if ( matches[ sel ] ) {
4457 matches.push( handleObj );
4460 if ( matches.length ) {
4461 handlerQueue.push({ elem: cur, handlers: matches });
4467 // Add the remaining (directly-bound) handlers
4468 if ( delegateCount < handlers.length ) {
4469 handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
4472 return handlerQueue;
4475 // Includes some event props shared by KeyEvent and MouseEvent
4476 props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
4481 props: "char charCode key keyCode".split(" "),
4482 filter: function( event, original ) {
4484 // Add which for key events
4485 if ( event.which == null ) {
4486 event.which = original.charCode != null ? original.charCode : original.keyCode;
4494 props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
4495 filter: function( event, original ) {
4496 var eventDoc, doc, body,
4497 button = original.button;
4499 // Calculate pageX/Y if missing and clientX/Y available
4500 if ( event.pageX == null && original.clientX != null ) {
4501 eventDoc = event.target.ownerDocument || document;
4502 doc = eventDoc.documentElement;
4503 body = eventDoc.body;
4505 event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
4506 event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
4509 // Add which for click: 1 === left; 2 === middle; 3 === right
4510 // Note: button is not normalized, so don't use it
4511 if ( !event.which && button !== undefined ) {
4512 event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
4519 fix: function( event ) {
4520 if ( event[ jQuery.expando ] ) {
4524 // Create a writable copy of the event object and normalize some properties
4527 originalEvent = event,
4528 fixHook = this.fixHooks[ type ];
4531 this.fixHooks[ type ] = fixHook =
4532 rmouseEvent.test( type ) ? this.mouseHooks :
4533 rkeyEvent.test( type ) ? this.keyHooks :
4536 copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
4538 event = new jQuery.Event( originalEvent );
4543 event[ prop ] = originalEvent[ prop ];
4546 // Support: Cordova 2.5 (WebKit) (#13255)
4547 // All events should have a target; Cordova deviceready doesn't
4548 if ( !event.target ) {
4549 event.target = document;
4552 // Support: Safari 6.0+, Chrome < 28
4553 // Target should not be a text node (#504, #13143)
4554 if ( event.target.nodeType === 3 ) {
4555 event.target = event.target.parentNode;
4558 return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
4563 // Prevent triggered image.load events from bubbling to window.load
4567 // Fire native event if possible so blur/focus sequence is correct
4568 trigger: function() {
4569 if ( this !== safeActiveElement() && this.focus ) {
4574 delegateType: "focusin"
4577 trigger: function() {
4578 if ( this === safeActiveElement() && this.blur ) {
4583 delegateType: "focusout"
4586 // For checkbox, fire native event so checked state will be right
4587 trigger: function() {
4588 if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
4594 // For cross-browser consistency, don't fire native .click() on links
4595 _default: function( event ) {
4596 return jQuery.nodeName( event.target, "a" );
4601 postDispatch: function( event ) {
4603 // Support: Firefox 20+
4604 // Firefox doesn't alert if the returnValue field is not set.
4605 if ( event.result !== undefined && event.originalEvent ) {
4606 event.originalEvent.returnValue = event.result;
4612 simulate: function( type, elem, event, bubble ) {
4613 // Piggyback on a donor event to simulate a different one.
4614 // Fake originalEvent to avoid donor's stopPropagation, but if the
4615 // simulated event prevents default then we do the same on the donor.
4616 var e = jQuery.extend(
4626 jQuery.event.trigger( e, null, elem );
4628 jQuery.event.dispatch.call( elem, e );
4630 if ( e.isDefaultPrevented() ) {
4631 event.preventDefault();
4636 jQuery.removeEvent = function( elem, type, handle ) {
4637 if ( elem.removeEventListener ) {
4638 elem.removeEventListener( type, handle, false );
4642 jQuery.Event = function( src, props ) {
4643 // Allow instantiation without the 'new' keyword
4644 if ( !(this instanceof jQuery.Event) ) {
4645 return new jQuery.Event( src, props );
4649 if ( src && src.type ) {
4650 this.originalEvent = src;
4651 this.type = src.type;
4653 // Events bubbling up the document may have been marked as prevented
4654 // by a handler lower down the tree; reflect the correct value.
4655 this.isDefaultPrevented = src.defaultPrevented ||
4656 src.defaultPrevented === undefined &&
4657 // Support: Android < 4.0
4658 src.returnValue === false ?
4667 // Put explicitly provided properties onto the event object
4669 jQuery.extend( this, props );
4672 // Create a timestamp if incoming event doesn't have one
4673 this.timeStamp = src && src.timeStamp || jQuery.now();
4676 this[ jQuery.expando ] = true;
4679 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
4680 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
4681 jQuery.Event.prototype = {
4682 isDefaultPrevented: returnFalse,
4683 isPropagationStopped: returnFalse,
4684 isImmediatePropagationStopped: returnFalse,
4686 preventDefault: function() {
4687 var e = this.originalEvent;
4689 this.isDefaultPrevented = returnTrue;
4691 if ( e && e.preventDefault ) {
4695 stopPropagation: function() {
4696 var e = this.originalEvent;
4698 this.isPropagationStopped = returnTrue;
4700 if ( e && e.stopPropagation ) {
4701 e.stopPropagation();
4704 stopImmediatePropagation: function() {
4705 var e = this.originalEvent;
4707 this.isImmediatePropagationStopped = returnTrue;
4709 if ( e && e.stopImmediatePropagation ) {
4710 e.stopImmediatePropagation();
4713 this.stopPropagation();
4717 // Create mouseenter/leave events using mouseover/out and event-time checks
4718 // Support: Chrome 15+
4720 mouseenter: "mouseover",
4721 mouseleave: "mouseout",
4722 pointerenter: "pointerover",
4723 pointerleave: "pointerout"
4724 }, function( orig, fix ) {
4725 jQuery.event.special[ orig ] = {
4729 handle: function( event ) {
4732 related = event.relatedTarget,
4733 handleObj = event.handleObj;
4735 // For mousenter/leave call the handler if related is outside the target.
4736 // NB: No relatedTarget if the mouse left/entered the browser window
4737 if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
4738 event.type = handleObj.origType;
4739 ret = handleObj.handler.apply( this, arguments );
4747 // Create "bubbling" focus and blur events
4748 // Support: Firefox, Chrome, Safari
4749 if ( !support.focusinBubbles ) {
4750 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
4752 // Attach a single capturing handler on the document while someone wants focusin/focusout
4753 var handler = function( event ) {
4754 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
4757 jQuery.event.special[ fix ] = {
4759 var doc = this.ownerDocument || this,
4760 attaches = data_priv.access( doc, fix );
4763 doc.addEventListener( orig, handler, true );
4765 data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
4767 teardown: function() {
4768 var doc = this.ownerDocument || this,
4769 attaches = data_priv.access( doc, fix ) - 1;
4772 doc.removeEventListener( orig, handler, true );
4773 data_priv.remove( doc, fix );
4776 data_priv.access( doc, fix, attaches );
4785 on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
4788 // Types can be a map of types/handlers
4789 if ( typeof types === "object" ) {
4790 // ( types-Object, selector, data )
4791 if ( typeof selector !== "string" ) {
4792 // ( types-Object, data )
4793 data = data || selector;
4794 selector = undefined;
4796 for ( type in types ) {
4797 this.on( type, selector, data, types[ type ], one );
4802 if ( data == null && fn == null ) {
4805 data = selector = undefined;
4806 } else if ( fn == null ) {
4807 if ( typeof selector === "string" ) {
4808 // ( types, selector, fn )
4812 // ( types, data, fn )
4815 selector = undefined;
4818 if ( fn === false ) {
4826 fn = function( event ) {
4827 // Can use an empty set, since event contains the info
4828 jQuery().off( event );
4829 return origFn.apply( this, arguments );
4831 // Use same guid so caller can remove using origFn
4832 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
4834 return this.each( function() {
4835 jQuery.event.add( this, types, fn, data, selector );
4838 one: function( types, selector, data, fn ) {
4839 return this.on( types, selector, data, fn, 1 );
4841 off: function( types, selector, fn ) {
4842 var handleObj, type;
4843 if ( types && types.preventDefault && types.handleObj ) {
4844 // ( event ) dispatched jQuery.Event
4845 handleObj = types.handleObj;
4846 jQuery( types.delegateTarget ).off(
4847 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
4853 if ( typeof types === "object" ) {
4854 // ( types-object [, selector] )
4855 for ( type in types ) {
4856 this.off( type, selector, types[ type ] );
4860 if ( selector === false || typeof selector === "function" ) {
4863 selector = undefined;
4865 if ( fn === false ) {
4868 return this.each(function() {
4869 jQuery.event.remove( this, types, fn, selector );
4873 trigger: function( type, data ) {
4874 return this.each(function() {
4875 jQuery.event.trigger( type, data, this );
4878 triggerHandler: function( type, data ) {
4881 return jQuery.event.trigger( type, data, elem, true );
4888 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
4889 rtagName = /<([\w:]+)/,
4890 rhtml = /<|&#?\w+;/,
4891 rnoInnerhtml = /<(?:script|style|link)/i,
4892 // checked="checked" or checked
4893 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
4894 rscriptType = /^$|\/(?:java|ecma)script/i,
4895 rscriptTypeMasked = /^true\/(.*)/,
4896 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
4898 // We have to close these tags to support XHTML (#13200)
4902 option: [ 1, "<select multiple='multiple'>", "</select>" ],
4904 thead: [ 1, "<table>", "</table>" ],
4905 col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
4906 tr: [ 2, "<table><tbody>", "</tbody></table>" ],
4907 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
4909 _default: [ 0, "", "" ]
4913 wrapMap.optgroup = wrapMap.option;
4915 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
4916 wrapMap.th = wrapMap.td;
4918 // Support: 1.x compatibility
4919 // Manipulating tables requires a tbody
4920 function manipulationTarget( elem, content ) {
4921 return jQuery.nodeName( elem, "table" ) &&
4922 jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
4924 elem.getElementsByTagName("tbody")[0] ||
4925 elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
4929 // Replace/restore the type attribute of script elements for safe DOM manipulation
4930 function disableScript( elem ) {
4931 elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
4934 function restoreScript( elem ) {
4935 var match = rscriptTypeMasked.exec( elem.type );
4938 elem.type = match[ 1 ];
4940 elem.removeAttribute("type");
4946 // Mark scripts as having already been evaluated
4947 function setGlobalEval( elems, refElements ) {
4951 for ( ; i < l; i++ ) {
4953 elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
4958 function cloneCopyEvent( src, dest ) {
4959 var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
4961 if ( dest.nodeType !== 1 ) {
4965 // 1. Copy private data: events, handlers, etc.
4966 if ( data_priv.hasData( src ) ) {
4967 pdataOld = data_priv.access( src );
4968 pdataCur = data_priv.set( dest, pdataOld );
4969 events = pdataOld.events;
4972 delete pdataCur.handle;
4973 pdataCur.events = {};
4975 for ( type in events ) {
4976 for ( i = 0, l = events[ type ].length; i < l; i++ ) {
4977 jQuery.event.add( dest, type, events[ type ][ i ] );
4983 // 2. Copy user data
4984 if ( data_user.hasData( src ) ) {
4985 udataOld = data_user.access( src );
4986 udataCur = jQuery.extend( {}, udataOld );
4988 data_user.set( dest, udataCur );
4992 function getAll( context, tag ) {
4993 var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
4994 context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
4997 return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
4998 jQuery.merge( [ context ], ret ) :
5003 function fixInput( src, dest ) {
5004 var nodeName = dest.nodeName.toLowerCase();
5006 // Fails to persist the checked state of a cloned checkbox or radio button.
5007 if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
5008 dest.checked = src.checked;
5010 // Fails to return the selected option to the default selected state when cloning options
5011 } else if ( nodeName === "input" || nodeName === "textarea" ) {
5012 dest.defaultValue = src.defaultValue;
5017 clone: function( elem, dataAndEvents, deepDataAndEvents ) {
5018 var i, l, srcElements, destElements,
5019 clone = elem.cloneNode( true ),
5020 inPage = jQuery.contains( elem.ownerDocument, elem );
5023 // Fix Cloning issues
5024 if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
5025 !jQuery.isXMLDoc( elem ) ) {
5027 // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
5028 destElements = getAll( clone );
5029 srcElements = getAll( elem );
5031 for ( i = 0, l = srcElements.length; i < l; i++ ) {
5032 fixInput( srcElements[ i ], destElements[ i ] );
5036 // Copy the events from the original to the clone
5037 if ( dataAndEvents ) {
5038 if ( deepDataAndEvents ) {
5039 srcElements = srcElements || getAll( elem );
5040 destElements = destElements || getAll( clone );
5042 for ( i = 0, l = srcElements.length; i < l; i++ ) {
5043 cloneCopyEvent( srcElements[ i ], destElements[ i ] );
5046 cloneCopyEvent( elem, clone );
5050 // Preserve script evaluation history
5051 destElements = getAll( clone, "script" );
5052 if ( destElements.length > 0 ) {
5053 setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
5056 // Return the cloned set
5060 buildFragment: function( elems, context, scripts, selection ) {
5061 var elem, tmp, tag, wrap, contains, j,
5062 fragment = context.createDocumentFragment(),
5067 for ( ; i < l; i++ ) {
5070 if ( elem || elem === 0 ) {
5072 // Add nodes directly
5073 if ( jQuery.type( elem ) === "object" ) {
5074 // Support: QtWebKit
5075 // jQuery.merge because push.apply(_, arraylike) throws
5076 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
5078 // Convert non-html into a text node
5079 } else if ( !rhtml.test( elem ) ) {
5080 nodes.push( context.createTextNode( elem ) );
5082 // Convert html into DOM nodes
5084 tmp = tmp || fragment.appendChild( context.createElement("div") );
5086 // Deserialize a standard representation
5087 tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
5088 wrap = wrapMap[ tag ] || wrapMap._default;
5089 tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
5091 // Descend through wrappers to the right content
5094 tmp = tmp.lastChild;
5097 // Support: QtWebKit
5098 // jQuery.merge because push.apply(_, arraylike) throws
5099 jQuery.merge( nodes, tmp.childNodes );
5101 // Remember the top-level container
5102 tmp = fragment.firstChild;
5105 // Support: Webkit, IE
5106 tmp.textContent = "";
5111 // Remove wrapper from fragment
5112 fragment.textContent = "";
5115 while ( (elem = nodes[ i++ ]) ) {
5117 // #4087 - If origin and destination elements are the same, and this is
5118 // that element, do not do anything
5119 if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
5123 contains = jQuery.contains( elem.ownerDocument, elem );
5125 // Append to fragment
5126 tmp = getAll( fragment.appendChild( elem ), "script" );
5128 // Preserve script evaluation history
5130 setGlobalEval( tmp );
5133 // Capture executables
5136 while ( (elem = tmp[ j++ ]) ) {
5137 if ( rscriptType.test( elem.type || "" ) ) {
5138 scripts.push( elem );
5147 cleanData: function( elems ) {
5148 var data, elem, type, key,
5149 special = jQuery.event.special,
5152 for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
5153 if ( jQuery.acceptData( elem ) ) {
5154 key = elem[ data_priv.expando ];
5156 if ( key && (data = data_priv.cache[ key ]) ) {
5157 if ( data.events ) {
5158 for ( type in data.events ) {
5159 if ( special[ type ] ) {
5160 jQuery.event.remove( elem, type );
5162 // This is a shortcut to avoid jQuery.event.remove's overhead
5164 jQuery.removeEvent( elem, type, data.handle );
5168 if ( data_priv.cache[ key ] ) {
5169 // Discard any remaining `private` data
5170 delete data_priv.cache[ key ];
5174 // Discard any remaining `user` data
5175 delete data_user.cache[ elem[ data_user.expando ] ];
5181 text: function( value ) {
5182 return access( this, function( value ) {
5183 return value === undefined ?
5184 jQuery.text( this ) :
5185 this.empty().each(function() {
5186 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5187 this.textContent = value;
5190 }, null, value, arguments.length );
5193 append: function() {
5194 return this.domManip( arguments, function( elem ) {
5195 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5196 var target = manipulationTarget( this, elem );
5197 target.appendChild( elem );
5202 prepend: function() {
5203 return this.domManip( arguments, function( elem ) {
5204 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5205 var target = manipulationTarget( this, elem );
5206 target.insertBefore( elem, target.firstChild );
5211 before: function() {
5212 return this.domManip( arguments, function( elem ) {
5213 if ( this.parentNode ) {
5214 this.parentNode.insertBefore( elem, this );
5220 return this.domManip( arguments, function( elem ) {
5221 if ( this.parentNode ) {
5222 this.parentNode.insertBefore( elem, this.nextSibling );
5227 remove: function( selector, keepData /* Internal Use Only */ ) {
5229 elems = selector ? jQuery.filter( selector, this ) : this,
5232 for ( ; (elem = elems[i]) != null; i++ ) {
5233 if ( !keepData && elem.nodeType === 1 ) {
5234 jQuery.cleanData( getAll( elem ) );
5237 if ( elem.parentNode ) {
5238 if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
5239 setGlobalEval( getAll( elem, "script" ) );
5241 elem.parentNode.removeChild( elem );
5252 for ( ; (elem = this[i]) != null; i++ ) {
5253 if ( elem.nodeType === 1 ) {
5255 // Prevent memory leaks
5256 jQuery.cleanData( getAll( elem, false ) );
5258 // Remove any remaining nodes
5259 elem.textContent = "";
5266 clone: function( dataAndEvents, deepDataAndEvents ) {
5267 dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
5268 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
5270 return this.map(function() {
5271 return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
5275 html: function( value ) {
5276 return access( this, function( value ) {
5277 var elem = this[ 0 ] || {},
5281 if ( value === undefined && elem.nodeType === 1 ) {
5282 return elem.innerHTML;
5285 // See if we can take a shortcut and just use innerHTML
5286 if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
5287 !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
5289 value = value.replace( rxhtmlTag, "<$1></$2>" );
5292 for ( ; i < l; i++ ) {
5293 elem = this[ i ] || {};
5295 // Remove element nodes and prevent memory leaks
5296 if ( elem.nodeType === 1 ) {
5297 jQuery.cleanData( getAll( elem, false ) );
5298 elem.innerHTML = value;
5304 // If using innerHTML throws an exception, use the fallback method
5309 this.empty().append( value );
5311 }, null, value, arguments.length );
5314 replaceWith: function() {
5315 var arg = arguments[ 0 ];
5317 // Make the changes, replacing each context element with the new content
5318 this.domManip( arguments, function( elem ) {
5319 arg = this.parentNode;
5321 jQuery.cleanData( getAll( this ) );
5324 arg.replaceChild( elem, this );
5328 // Force removal if there was no new content (e.g., from empty arguments)
5329 return arg && (arg.length || arg.nodeType) ? this : this.remove();
5332 detach: function( selector ) {
5333 return this.remove( selector, true );
5336 domManip: function( args, callback ) {
5338 // Flatten any nested arrays
5339 args = concat.apply( [], args );
5341 var fragment, first, scripts, hasScripts, node, doc,
5347 isFunction = jQuery.isFunction( value );
5349 // We can't cloneNode fragments that contain checked, in WebKit
5351 ( l > 1 && typeof value === "string" &&
5352 !support.checkClone && rchecked.test( value ) ) ) {
5353 return this.each(function( index ) {
5354 var self = set.eq( index );
5356 args[ 0 ] = value.call( this, index, self.html() );
5358 self.domManip( args, callback );
5363 fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
5364 first = fragment.firstChild;
5366 if ( fragment.childNodes.length === 1 ) {
5371 scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
5372 hasScripts = scripts.length;
5374 // Use the original fragment for the last item instead of the first because it can end up
5375 // being emptied incorrectly in certain situations (#8070).
5376 for ( ; i < l; i++ ) {
5379 if ( i !== iNoClone ) {
5380 node = jQuery.clone( node, true, true );
5382 // Keep references to cloned scripts for later restoration
5384 // Support: QtWebKit
5385 // jQuery.merge because push.apply(_, arraylike) throws
5386 jQuery.merge( scripts, getAll( node, "script" ) );
5390 callback.call( this[ i ], node, i );
5394 doc = scripts[ scripts.length - 1 ].ownerDocument;
5397 jQuery.map( scripts, restoreScript );
5399 // Evaluate executable scripts on first document insertion
5400 for ( i = 0; i < hasScripts; i++ ) {
5401 node = scripts[ i ];
5402 if ( rscriptType.test( node.type || "" ) &&
5403 !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
5406 // Optional AJAX dependency, but won't run scripts if not present
5407 if ( jQuery._evalUrl ) {
5408 jQuery._evalUrl( node.src );
5411 jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
5425 prependTo: "prepend",
5426 insertBefore: "before",
5427 insertAfter: "after",
5428 replaceAll: "replaceWith"
5429 }, function( name, original ) {
5430 jQuery.fn[ name ] = function( selector ) {
5433 insert = jQuery( selector ),
5434 last = insert.length - 1,
5437 for ( ; i <= last; i++ ) {
5438 elems = i === last ? this : this.clone( true );
5439 jQuery( insert[ i ] )[ original ]( elems );
5441 // Support: QtWebKit
5442 // .get() because push.apply(_, arraylike) throws
5443 push.apply( ret, elems.get() );
5446 return this.pushStack( ret );
5455 * Retrieve the actual display of a element
5456 * @param {String} name nodeName of the element
5457 * @param {Object} doc Document object
5459 // Called only from within defaultDisplay
5460 function actualDisplay( name, doc ) {
5462 elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
5464 // getDefaultComputedStyle might be reliably used only on attached element
5465 display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
5467 // Use of this method is a temporary fix (more like optmization) until something better comes along,
5468 // since it was removed from specification and supported only in FF
5469 style.display : jQuery.css( elem[ 0 ], "display" );
5471 // We don't have any data stored on the element,
5472 // so use "detach" method as fast way to get rid of the element
5479 * Try to determine the default display value of an element
5480 * @param {String} nodeName
5482 function defaultDisplay( nodeName ) {
5484 display = elemdisplay[ nodeName ];
5487 display = actualDisplay( nodeName, doc );
5489 // If the simple way fails, read from inside an iframe
5490 if ( display === "none" || !display ) {
5492 // Use the already-created iframe if possible
5493 iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
5495 // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
5496 doc = iframe[ 0 ].contentDocument;
5502 display = actualDisplay( nodeName, doc );
5506 // Store the correct default display
5507 elemdisplay[ nodeName ] = display;
5512 var rmargin = (/^margin/);
5514 var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
5516 var getStyles = function( elem ) {
5517 return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
5522 function curCSS( elem, name, computed ) {
5523 var width, minWidth, maxWidth, ret,
5526 computed = computed || getStyles( elem );
5529 // getPropertyValue is only needed for .css('filter') in IE9, see #12537
5531 ret = computed.getPropertyValue( name ) || computed[ name ];
5536 if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
5537 ret = jQuery.style( elem, name );
5541 // A tribute to the "awesome hack by Dean Edwards"
5542 // iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
5543 // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
5544 if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
5546 // Remember the original values
5547 width = style.width;
5548 minWidth = style.minWidth;
5549 maxWidth = style.maxWidth;
5551 // Put in the new values to get a computed value out
5552 style.minWidth = style.maxWidth = style.width = ret;
5553 ret = computed.width;
5555 // Revert the changed values
5556 style.width = width;
5557 style.minWidth = minWidth;
5558 style.maxWidth = maxWidth;
5562 return ret !== undefined ?
5564 // IE returns zIndex value as an integer.
5570 function addGetHookIf( conditionFn, hookFn ) {
5571 // Define the hook, we'll check on the first run if it's really needed.
5574 if ( conditionFn() ) {
5575 // Hook not needed (or it's not possible to use it due to missing dependency),
5577 // Since there are no other hooks for marginRight, remove the whole object.
5582 // Hook needed; redefine it so that the support test is not executed again.
5584 return (this.get = hookFn).apply( this, arguments );
5591 var pixelPositionVal, boxSizingReliableVal,
5592 docElem = document.documentElement,
5593 container = document.createElement( "div" ),
5594 div = document.createElement( "div" );
5600 div.style.backgroundClip = "content-box";
5601 div.cloneNode( true ).style.backgroundClip = "";
5602 support.clearCloneStyle = div.style.backgroundClip === "content-box";
5604 container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" +
5605 "position:absolute";
5606 container.appendChild( div );
5608 // Executing both pixelPosition & boxSizingReliable tests require only one layout
5609 // so they're executed at the same time to save the second computation.
5610 function computePixelPositionAndBoxSizingReliable() {
5612 // Support: Firefox<29, Android 2.3
5613 // Vendor-prefix box-sizing
5614 "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
5615 "box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
5616 "border:1px;padding:1px;width:4px;position:absolute";
5618 docElem.appendChild( container );
5620 var divStyle = window.getComputedStyle( div, null );
5621 pixelPositionVal = divStyle.top !== "1%";
5622 boxSizingReliableVal = divStyle.width === "4px";
5624 docElem.removeChild( container );
5627 // Support: node.js jsdom
5628 // Don't assume that getComputedStyle is a property of the global object
5629 if ( window.getComputedStyle ) {
5630 jQuery.extend( support, {
5631 pixelPosition: function() {
5632 // This test is executed only once but we still do memoizing
5633 // since we can use the boxSizingReliable pre-computing.
5634 // No need to check if the test was already performed, though.
5635 computePixelPositionAndBoxSizingReliable();
5636 return pixelPositionVal;
5638 boxSizingReliable: function() {
5639 if ( boxSizingReliableVal == null ) {
5640 computePixelPositionAndBoxSizingReliable();
5642 return boxSizingReliableVal;
5644 reliableMarginRight: function() {
5645 // Support: Android 2.3
5646 // Check if div with explicit width and no margin-right incorrectly
5647 // gets computed margin-right based on width of container. (#3333)
5648 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
5649 // This support function is only executed once so no memoizing is needed.
5651 marginDiv = div.appendChild( document.createElement( "div" ) );
5653 // Reset CSS: box-sizing; display; margin; border; padding
5654 marginDiv.style.cssText = div.style.cssText =
5655 // Support: Firefox<29, Android 2.3
5656 // Vendor-prefix box-sizing
5657 "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
5658 "box-sizing:content-box;display:block;margin:0;border:0;padding:0";
5659 marginDiv.style.marginRight = marginDiv.style.width = "0";
5660 div.style.width = "1px";
5661 docElem.appendChild( container );
5663 ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight );
5665 docElem.removeChild( container );
5674 // A method for quickly swapping in/out CSS properties to get correct calculations.
5675 jQuery.swap = function( elem, options, callback, args ) {
5679 // Remember the old values, and insert the new ones
5680 for ( name in options ) {
5681 old[ name ] = elem.style[ name ];
5682 elem.style[ name ] = options[ name ];
5685 ret = callback.apply( elem, args || [] );
5687 // Revert the old values
5688 for ( name in options ) {
5689 elem.style[ name ] = old[ name ];
5697 // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
5698 // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
5699 rdisplayswap = /^(none|table(?!-c[ea]).+)/,
5700 rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
5701 rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
5703 cssShow = { position: "absolute", visibility: "hidden", display: "block" },
5704 cssNormalTransform = {
5709 cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
5711 // return a css property mapped to a potentially vendor prefixed property
5712 function vendorPropName( style, name ) {
5714 // shortcut for names that are not vendor prefixed
5715 if ( name in style ) {
5719 // check for vendor prefixed names
5720 var capName = name[0].toUpperCase() + name.slice(1),
5722 i = cssPrefixes.length;
5725 name = cssPrefixes[ i ] + capName;
5726 if ( name in style ) {
5734 function setPositiveNumber( elem, value, subtract ) {
5735 var matches = rnumsplit.exec( value );
5737 // Guard against undefined "subtract", e.g., when used as in cssHooks
5738 Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
5742 function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
5743 var i = extra === ( isBorderBox ? "border" : "content" ) ?
5744 // If we already have the right measurement, avoid augmentation
5746 // Otherwise initialize for horizontal or vertical properties
5747 name === "width" ? 1 : 0,
5751 for ( ; i < 4; i += 2 ) {
5752 // both box models exclude margin, so add it if we want it
5753 if ( extra === "margin" ) {
5754 val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
5757 if ( isBorderBox ) {
5758 // border-box includes padding, so remove it if we want content
5759 if ( extra === "content" ) {
5760 val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
5763 // at this point, extra isn't border nor margin, so remove border
5764 if ( extra !== "margin" ) {
5765 val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
5768 // at this point, extra isn't content, so add padding
5769 val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
5771 // at this point, extra isn't content nor padding, so add border
5772 if ( extra !== "padding" ) {
5773 val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
5781 function getWidthOrHeight( elem, name, extra ) {
5783 // Start with offset property, which is equivalent to the border-box value
5784 var valueIsBorderBox = true,
5785 val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
5786 styles = getStyles( elem ),
5787 isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
5789 // some non-html elements return undefined for offsetWidth, so check for null/undefined
5790 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
5791 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
5792 if ( val <= 0 || val == null ) {
5793 // Fall back to computed then uncomputed css if necessary
5794 val = curCSS( elem, name, styles );
5795 if ( val < 0 || val == null ) {
5796 val = elem.style[ name ];
5799 // Computed unit is not pixels. Stop here and return.
5800 if ( rnumnonpx.test(val) ) {
5804 // we need the check for style in case a browser which returns unreliable values
5805 // for getComputedStyle silently falls back to the reliable elem.style
5806 valueIsBorderBox = isBorderBox &&
5807 ( support.boxSizingReliable() || val === elem.style[ name ] );
5809 // Normalize "", auto, and prepare for extra
5810 val = parseFloat( val ) || 0;
5813 // use the active box-sizing model to add/subtract irrelevant styles
5815 augmentWidthOrHeight(
5818 extra || ( isBorderBox ? "border" : "content" ),
5825 function showHide( elements, show ) {
5826 var display, elem, hidden,
5829 length = elements.length;
5831 for ( ; index < length; index++ ) {
5832 elem = elements[ index ];
5833 if ( !elem.style ) {
5837 values[ index ] = data_priv.get( elem, "olddisplay" );
5838 display = elem.style.display;
5840 // Reset the inline display of this element to learn if it is
5841 // being hidden by cascaded rules or not
5842 if ( !values[ index ] && display === "none" ) {
5843 elem.style.display = "";
5846 // Set elements which have been overridden with display: none
5847 // in a stylesheet to whatever the default browser style is
5848 // for such an element
5849 if ( elem.style.display === "" && isHidden( elem ) ) {
5850 values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) );
5853 hidden = isHidden( elem );
5855 if ( display !== "none" || !hidden ) {
5856 data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
5861 // Set the display of most of the elements in a second loop
5862 // to avoid the constant reflow
5863 for ( index = 0; index < length; index++ ) {
5864 elem = elements[ index ];
5865 if ( !elem.style ) {
5868 if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
5869 elem.style.display = show ? values[ index ] || "" : "none";
5877 // Add in style property hooks for overriding the default
5878 // behavior of getting and setting a style property
5881 get: function( elem, computed ) {
5883 // We should always get a number back from opacity
5884 var ret = curCSS( elem, "opacity" );
5885 return ret === "" ? "1" : ret;
5891 // Don't automatically add "px" to these possibly-unitless properties
5893 "columnCount": true,
5894 "fillOpacity": true,
5907 // Add in properties whose names you wish to fix before
5908 // setting or getting the value
5910 // normalize float css property
5914 // Get and set the style property on a DOM Node
5915 style: function( elem, name, value, extra ) {
5916 // Don't set styles on text and comment nodes
5917 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
5921 // Make sure that we're working with the right name
5922 var ret, type, hooks,
5923 origName = jQuery.camelCase( name ),
5926 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
5928 // gets hook for the prefixed version
5929 // followed by the unprefixed version
5930 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
5932 // Check if we're setting a value
5933 if ( value !== undefined ) {
5934 type = typeof value;
5936 // convert relative number strings (+= or -=) to relative numbers. #7345
5937 if ( type === "string" && (ret = rrelNum.exec( value )) ) {
5938 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
5943 // Make sure that null and NaN values aren't set. See: #7116
5944 if ( value == null || value !== value ) {
5948 // If a number was passed in, add 'px' to the (except for certain CSS properties)
5949 if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
5953 // Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
5954 // but it would mean to define eight (for every problematic property) identical functions
5955 if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
5956 style[ name ] = "inherit";
5959 // If a hook was provided, use that value, otherwise just set the specified value
5960 if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
5961 style[ name ] = value;
5965 // If a hook was provided get the non-computed value from there
5966 if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
5970 // Otherwise just get the value from the style object
5971 return style[ name ];
5975 css: function( elem, name, extra, styles ) {
5976 var val, num, hooks,
5977 origName = jQuery.camelCase( name );
5979 // Make sure that we're working with the right name
5980 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
5982 // gets hook for the prefixed version
5983 // followed by the unprefixed version
5984 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
5986 // If a hook was provided get the computed value from there
5987 if ( hooks && "get" in hooks ) {
5988 val = hooks.get( elem, true, extra );
5991 // Otherwise, if a way to get the computed value exists, use that
5992 if ( val === undefined ) {
5993 val = curCSS( elem, name, styles );
5996 //convert "normal" to computed value
5997 if ( val === "normal" && name in cssNormalTransform ) {
5998 val = cssNormalTransform[ name ];
6001 // Return, converting to number if forced or a qualifier was provided and val looks numeric
6002 if ( extra === "" || extra ) {
6003 num = parseFloat( val );
6004 return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
6010 jQuery.each([ "height", "width" ], function( i, name ) {
6011 jQuery.cssHooks[ name ] = {
6012 get: function( elem, computed, extra ) {
6014 // certain elements can have dimension info if we invisibly show them
6015 // however, it must have a current display style that would benefit from this
6016 return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
6017 jQuery.swap( elem, cssShow, function() {
6018 return getWidthOrHeight( elem, name, extra );
6020 getWidthOrHeight( elem, name, extra );
6024 set: function( elem, value, extra ) {
6025 var styles = extra && getStyles( elem );
6026 return setPositiveNumber( elem, value, extra ?
6027 augmentWidthOrHeight(
6031 jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
6039 // Support: Android 2.3
6040 jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
6041 function( elem, computed ) {
6043 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
6044 // Work around by temporarily setting element display to inline-block
6045 return jQuery.swap( elem, { "display": "inline-block" },
6046 curCSS, [ elem, "marginRight" ] );
6051 // These hooks are used by animate to expand properties
6056 }, function( prefix, suffix ) {
6057 jQuery.cssHooks[ prefix + suffix ] = {
6058 expand: function( value ) {
6062 // assumes a single number if not a string
6063 parts = typeof value === "string" ? value.split(" ") : [ value ];
6065 for ( ; i < 4; i++ ) {
6066 expanded[ prefix + cssExpand[ i ] + suffix ] =
6067 parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
6074 if ( !rmargin.test( prefix ) ) {
6075 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
6080 css: function( name, value ) {
6081 return access( this, function( elem, name, value ) {
6086 if ( jQuery.isArray( name ) ) {
6087 styles = getStyles( elem );
6090 for ( ; i < len; i++ ) {
6091 map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
6097 return value !== undefined ?
6098 jQuery.style( elem, name, value ) :
6099 jQuery.css( elem, name );
6100 }, name, value, arguments.length > 1 );
6103 return showHide( this, true );
6106 return showHide( this );
6108 toggle: function( state ) {
6109 if ( typeof state === "boolean" ) {
6110 return state ? this.show() : this.hide();
6113 return this.each(function() {
6114 if ( isHidden( this ) ) {
6115 jQuery( this ).show();
6117 jQuery( this ).hide();
6124 function Tween( elem, options, prop, end, easing ) {
6125 return new Tween.prototype.init( elem, options, prop, end, easing );
6127 jQuery.Tween = Tween;
6131 init: function( elem, options, prop, end, easing, unit ) {
6134 this.easing = easing || "swing";
6135 this.options = options;
6136 this.start = this.now = this.cur();
6138 this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
6141 var hooks = Tween.propHooks[ this.prop ];
6143 return hooks && hooks.get ?
6145 Tween.propHooks._default.get( this );
6147 run: function( percent ) {
6149 hooks = Tween.propHooks[ this.prop ];
6151 if ( this.options.duration ) {
6152 this.pos = eased = jQuery.easing[ this.easing ](
6153 percent, this.options.duration * percent, 0, 1, this.options.duration
6156 this.pos = eased = percent;
6158 this.now = ( this.end - this.start ) * eased + this.start;
6160 if ( this.options.step ) {
6161 this.options.step.call( this.elem, this.now, this );
6164 if ( hooks && hooks.set ) {
6167 Tween.propHooks._default.set( this );
6173 Tween.prototype.init.prototype = Tween.prototype;
6177 get: function( tween ) {
6180 if ( tween.elem[ tween.prop ] != null &&
6181 (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
6182 return tween.elem[ tween.prop ];
6185 // passing an empty string as a 3rd parameter to .css will automatically
6186 // attempt a parseFloat and fallback to a string if the parse fails
6187 // so, simple values such as "10px" are parsed to Float.
6188 // complex values such as "rotate(1rad)" are returned as is.
6189 result = jQuery.css( tween.elem, tween.prop, "" );
6190 // Empty strings, null, undefined and "auto" are converted to 0.
6191 return !result || result === "auto" ? 0 : result;
6193 set: function( tween ) {
6194 // use step hook for back compat - use cssHook if its there - use .style if its
6195 // available and use plain properties where available
6196 if ( jQuery.fx.step[ tween.prop ] ) {
6197 jQuery.fx.step[ tween.prop ]( tween );
6198 } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
6199 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
6201 tween.elem[ tween.prop ] = tween.now;
6208 // Panic based approach to setting things on disconnected nodes
6210 Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
6211 set: function( tween ) {
6212 if ( tween.elem.nodeType && tween.elem.parentNode ) {
6213 tween.elem[ tween.prop ] = tween.now;
6219 linear: function( p ) {
6222 swing: function( p ) {
6223 return 0.5 - Math.cos( p * Math.PI ) / 2;
6227 jQuery.fx = Tween.prototype.init;
6229 // Back Compat <1.8 extension point
6230 jQuery.fx.step = {};
6237 rfxtypes = /^(?:toggle|show|hide)$/,
6238 rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
6239 rrun = /queueHooks$/,
6240 animationPrefilters = [ defaultPrefilter ],
6242 "*": [ function( prop, value ) {
6243 var tween = this.createTween( prop, value ),
6244 target = tween.cur(),
6245 parts = rfxnum.exec( value ),
6246 unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
6248 // Starting value computation is required for potential unit mismatches
6249 start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
6250 rfxnum.exec( jQuery.css( tween.elem, prop ) ),
6254 if ( start && start[ 3 ] !== unit ) {
6255 // Trust units reported by jQuery.css
6256 unit = unit || start[ 3 ];
6258 // Make sure we update the tween properties later on
6259 parts = parts || [];
6261 // Iteratively approximate from a nonzero starting point
6262 start = +target || 1;
6265 // If previous iteration zeroed out, double until we get *something*
6266 // Use a string for doubling factor so we don't accidentally see scale as unchanged below
6267 scale = scale || ".5";
6270 start = start / scale;
6271 jQuery.style( tween.elem, prop, start + unit );
6273 // Update scale, tolerating zero or NaN from tween.cur()
6274 // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
6275 } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
6278 // Update tween properties
6280 start = tween.start = +start || +target || 0;
6282 // If a +=/-= token was provided, we're doing a relative animation
6283 tween.end = parts[ 1 ] ?
6284 start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
6292 // Animations created synchronously will run synchronously
6293 function createFxNow() {
6294 setTimeout(function() {
6297 return ( fxNow = jQuery.now() );
6300 // Generate parameters to create a standard animation
6301 function genFx( type, includeWidth ) {
6304 attrs = { height: type };
6306 // if we include width, step value is 1 to do all cssExpand values,
6307 // if we don't include width, step value is 2 to skip over Left and Right
6308 includeWidth = includeWidth ? 1 : 0;
6309 for ( ; i < 4 ; i += 2 - includeWidth ) {
6310 which = cssExpand[ i ];
6311 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
6314 if ( includeWidth ) {
6315 attrs.opacity = attrs.width = type;
6321 function createTween( value, prop, animation ) {
6323 collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
6325 length = collection.length;
6326 for ( ; index < length; index++ ) {
6327 if ( (tween = collection[ index ].call( animation, prop, value )) ) {
6329 // we're done with this property
6335 function defaultPrefilter( elem, props, opts ) {
6336 /* jshint validthis: true */
6337 var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
6341 hidden = elem.nodeType && isHidden( elem ),
6342 dataShow = data_priv.get( elem, "fxshow" );
6344 // handle queue: false promises
6345 if ( !opts.queue ) {
6346 hooks = jQuery._queueHooks( elem, "fx" );
6347 if ( hooks.unqueued == null ) {
6349 oldfire = hooks.empty.fire;
6350 hooks.empty.fire = function() {
6351 if ( !hooks.unqueued ) {
6358 anim.always(function() {
6359 // doing this makes sure that the complete handler will be called
6360 // before this completes
6361 anim.always(function() {
6363 if ( !jQuery.queue( elem, "fx" ).length ) {
6370 // height/width overflow pass
6371 if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
6372 // Make sure that nothing sneaks out
6373 // Record all 3 overflow attributes because IE9-10 do not
6374 // change the overflow attribute when overflowX and
6375 // overflowY are set to the same value
6376 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
6378 // Set display property to inline-block for height/width
6379 // animations on inline elements that are having width/height animated
6380 display = jQuery.css( elem, "display" );
6382 // Test default display if display is currently "none"
6383 checkDisplay = display === "none" ?
6384 data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
6386 if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
6387 style.display = "inline-block";
6391 if ( opts.overflow ) {
6392 style.overflow = "hidden";
6393 anim.always(function() {
6394 style.overflow = opts.overflow[ 0 ];
6395 style.overflowX = opts.overflow[ 1 ];
6396 style.overflowY = opts.overflow[ 2 ];
6401 for ( prop in props ) {
6402 value = props[ prop ];
6403 if ( rfxtypes.exec( value ) ) {
6404 delete props[ prop ];
6405 toggle = toggle || value === "toggle";
6406 if ( value === ( hidden ? "hide" : "show" ) ) {
6408 // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
6409 if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
6415 orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
6417 // Any non-fx value stops us from restoring the original display value
6419 display = undefined;
6423 if ( !jQuery.isEmptyObject( orig ) ) {
6425 if ( "hidden" in dataShow ) {
6426 hidden = dataShow.hidden;
6429 dataShow = data_priv.access( elem, "fxshow", {} );
6432 // store state if its toggle - enables .stop().toggle() to "reverse"
6434 dataShow.hidden = !hidden;
6437 jQuery( elem ).show();
6439 anim.done(function() {
6440 jQuery( elem ).hide();
6443 anim.done(function() {
6446 data_priv.remove( elem, "fxshow" );
6447 for ( prop in orig ) {
6448 jQuery.style( elem, prop, orig[ prop ] );
6451 for ( prop in orig ) {
6452 tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
6454 if ( !( prop in dataShow ) ) {
6455 dataShow[ prop ] = tween.start;
6457 tween.end = tween.start;
6458 tween.start = prop === "width" || prop === "height" ? 1 : 0;
6463 // If this is a noop like .hide().hide(), restore an overwritten display value
6464 } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
6465 style.display = display;
6469 function propFilter( props, specialEasing ) {
6470 var index, name, easing, value, hooks;
6472 // camelCase, specialEasing and expand cssHook pass
6473 for ( index in props ) {
6474 name = jQuery.camelCase( index );
6475 easing = specialEasing[ name ];
6476 value = props[ index ];
6477 if ( jQuery.isArray( value ) ) {
6478 easing = value[ 1 ];
6479 value = props[ index ] = value[ 0 ];
6482 if ( index !== name ) {
6483 props[ name ] = value;
6484 delete props[ index ];
6487 hooks = jQuery.cssHooks[ name ];
6488 if ( hooks && "expand" in hooks ) {
6489 value = hooks.expand( value );
6490 delete props[ name ];
6492 // not quite $.extend, this wont overwrite keys already present.
6493 // also - reusing 'index' from above because we have the correct "name"
6494 for ( index in value ) {
6495 if ( !( index in props ) ) {
6496 props[ index ] = value[ index ];
6497 specialEasing[ index ] = easing;
6501 specialEasing[ name ] = easing;
6506 function Animation( elem, properties, options ) {
6510 length = animationPrefilters.length,
6511 deferred = jQuery.Deferred().always( function() {
6512 // don't match elem in the :animated selector
6519 var currentTime = fxNow || createFxNow(),
6520 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
6521 // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
6522 temp = remaining / animation.duration || 0,
6525 length = animation.tweens.length;
6527 for ( ; index < length ; index++ ) {
6528 animation.tweens[ index ].run( percent );
6531 deferred.notifyWith( elem, [ animation, percent, remaining ]);
6533 if ( percent < 1 && length ) {
6536 deferred.resolveWith( elem, [ animation ] );
6540 animation = deferred.promise({
6542 props: jQuery.extend( {}, properties ),
6543 opts: jQuery.extend( true, { specialEasing: {} }, options ),
6544 originalProperties: properties,
6545 originalOptions: options,
6546 startTime: fxNow || createFxNow(),
6547 duration: options.duration,
6549 createTween: function( prop, end ) {
6550 var tween = jQuery.Tween( elem, animation.opts, prop, end,
6551 animation.opts.specialEasing[ prop ] || animation.opts.easing );
6552 animation.tweens.push( tween );
6555 stop: function( gotoEnd ) {
6557 // if we are going to the end, we want to run all the tweens
6558 // otherwise we skip this part
6559 length = gotoEnd ? animation.tweens.length : 0;
6564 for ( ; index < length ; index++ ) {
6565 animation.tweens[ index ].run( 1 );
6568 // resolve when we played the last frame
6569 // otherwise, reject
6571 deferred.resolveWith( elem, [ animation, gotoEnd ] );
6573 deferred.rejectWith( elem, [ animation, gotoEnd ] );
6578 props = animation.props;
6580 propFilter( props, animation.opts.specialEasing );
6582 for ( ; index < length ; index++ ) {
6583 result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
6589 jQuery.map( props, createTween, animation );
6591 if ( jQuery.isFunction( animation.opts.start ) ) {
6592 animation.opts.start.call( elem, animation );
6596 jQuery.extend( tick, {
6599 queue: animation.opts.queue
6603 // attach callbacks from options
6604 return animation.progress( animation.opts.progress )
6605 .done( animation.opts.done, animation.opts.complete )
6606 .fail( animation.opts.fail )
6607 .always( animation.opts.always );
6610 jQuery.Animation = jQuery.extend( Animation, {
6612 tweener: function( props, callback ) {
6613 if ( jQuery.isFunction( props ) ) {
6617 props = props.split(" ");
6622 length = props.length;
6624 for ( ; index < length ; index++ ) {
6625 prop = props[ index ];
6626 tweeners[ prop ] = tweeners[ prop ] || [];
6627 tweeners[ prop ].unshift( callback );
6631 prefilter: function( callback, prepend ) {
6633 animationPrefilters.unshift( callback );
6635 animationPrefilters.push( callback );
6640 jQuery.speed = function( speed, easing, fn ) {
6641 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
6642 complete: fn || !fn && easing ||
6643 jQuery.isFunction( speed ) && speed,
6645 easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
6648 opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
6649 opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
6651 // normalize opt.queue - true/undefined/null -> "fx"
6652 if ( opt.queue == null || opt.queue === true ) {
6657 opt.old = opt.complete;
6659 opt.complete = function() {
6660 if ( jQuery.isFunction( opt.old ) ) {
6661 opt.old.call( this );
6665 jQuery.dequeue( this, opt.queue );
6673 fadeTo: function( speed, to, easing, callback ) {
6675 // show any hidden elements after setting opacity to 0
6676 return this.filter( isHidden ).css( "opacity", 0 ).show()
6678 // animate to the value specified
6679 .end().animate({ opacity: to }, speed, easing, callback );
6681 animate: function( prop, speed, easing, callback ) {
6682 var empty = jQuery.isEmptyObject( prop ),
6683 optall = jQuery.speed( speed, easing, callback ),
6684 doAnimation = function() {
6685 // Operate on a copy of prop so per-property easing won't be lost
6686 var anim = Animation( this, jQuery.extend( {}, prop ), optall );
6688 // Empty animations, or finishing resolves immediately
6689 if ( empty || data_priv.get( this, "finish" ) ) {
6693 doAnimation.finish = doAnimation;
6695 return empty || optall.queue === false ?
6696 this.each( doAnimation ) :
6697 this.queue( optall.queue, doAnimation );
6699 stop: function( type, clearQueue, gotoEnd ) {
6700 var stopQueue = function( hooks ) {
6701 var stop = hooks.stop;
6706 if ( typeof type !== "string" ) {
6707 gotoEnd = clearQueue;
6711 if ( clearQueue && type !== false ) {
6712 this.queue( type || "fx", [] );
6715 return this.each(function() {
6717 index = type != null && type + "queueHooks",
6718 timers = jQuery.timers,
6719 data = data_priv.get( this );
6722 if ( data[ index ] && data[ index ].stop ) {
6723 stopQueue( data[ index ] );
6726 for ( index in data ) {
6727 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
6728 stopQueue( data[ index ] );
6733 for ( index = timers.length; index--; ) {
6734 if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
6735 timers[ index ].anim.stop( gotoEnd );
6737 timers.splice( index, 1 );
6741 // start the next in the queue if the last step wasn't forced
6742 // timers currently will call their complete callbacks, which will dequeue
6743 // but only if they were gotoEnd
6744 if ( dequeue || !gotoEnd ) {
6745 jQuery.dequeue( this, type );
6749 finish: function( type ) {
6750 if ( type !== false ) {
6751 type = type || "fx";
6753 return this.each(function() {
6755 data = data_priv.get( this ),
6756 queue = data[ type + "queue" ],
6757 hooks = data[ type + "queueHooks" ],
6758 timers = jQuery.timers,
6759 length = queue ? queue.length : 0;
6761 // enable finishing flag on private data
6764 // empty the queue first
6765 jQuery.queue( this, type, [] );
6767 if ( hooks && hooks.stop ) {
6768 hooks.stop.call( this, true );
6771 // look for any active animations, and finish them
6772 for ( index = timers.length; index--; ) {
6773 if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
6774 timers[ index ].anim.stop( true );
6775 timers.splice( index, 1 );
6779 // look for any animations in the old queue and finish them
6780 for ( index = 0; index < length; index++ ) {
6781 if ( queue[ index ] && queue[ index ].finish ) {
6782 queue[ index ].finish.call( this );
6786 // turn off finishing flag
6792 jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
6793 var cssFn = jQuery.fn[ name ];
6794 jQuery.fn[ name ] = function( speed, easing, callback ) {
6795 return speed == null || typeof speed === "boolean" ?
6796 cssFn.apply( this, arguments ) :
6797 this.animate( genFx( name, true ), speed, easing, callback );
6801 // Generate shortcuts for custom animations
6803 slideDown: genFx("show"),
6804 slideUp: genFx("hide"),
6805 slideToggle: genFx("toggle"),
6806 fadeIn: { opacity: "show" },
6807 fadeOut: { opacity: "hide" },
6808 fadeToggle: { opacity: "toggle" }
6809 }, function( name, props ) {
6810 jQuery.fn[ name ] = function( speed, easing, callback ) {
6811 return this.animate( props, speed, easing, callback );
6816 jQuery.fx.tick = function() {
6819 timers = jQuery.timers;
6821 fxNow = jQuery.now();
6823 for ( ; i < timers.length; i++ ) {
6824 timer = timers[ i ];
6825 // Checks the timer has not already been removed
6826 if ( !timer() && timers[ i ] === timer ) {
6827 timers.splice( i--, 1 );
6831 if ( !timers.length ) {
6837 jQuery.fx.timer = function( timer ) {
6838 jQuery.timers.push( timer );
6842 jQuery.timers.pop();
6846 jQuery.fx.interval = 13;
6848 jQuery.fx.start = function() {
6850 timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
6854 jQuery.fx.stop = function() {
6855 clearInterval( timerId );
6859 jQuery.fx.speeds = {
6867 // Based off of the plugin by Clint Helfers, with permission.
6868 // http://blindsignals.com/index.php/2009/07/jquery-delay/
6869 jQuery.fn.delay = function( time, type ) {
6870 time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
6871 type = type || "fx";
6873 return this.queue( type, function( next, hooks ) {
6874 var timeout = setTimeout( next, time );
6875 hooks.stop = function() {
6876 clearTimeout( timeout );
6883 var input = document.createElement( "input" ),
6884 select = document.createElement( "select" ),
6885 opt = select.appendChild( document.createElement( "option" ) );
6887 input.type = "checkbox";
6889 // Support: iOS 5.1, Android 4.x, Android 2.3
6890 // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
6891 support.checkOn = input.value !== "";
6893 // Must access the parent to make an option select properly
6894 // Support: IE9, IE10
6895 support.optSelected = opt.selected;
6897 // Make sure that the options inside disabled selects aren't marked as disabled
6898 // (WebKit marks them as disabled)
6899 select.disabled = true;
6900 support.optDisabled = !opt.disabled;
6902 // Check if an input maintains its value after becoming a radio
6903 // Support: IE9, IE10
6904 input = document.createElement( "input" );
6906 input.type = "radio";
6907 support.radioValue = input.value === "t";
6911 var nodeHook, boolHook,
6912 attrHandle = jQuery.expr.attrHandle;
6915 attr: function( name, value ) {
6916 return access( this, jQuery.attr, name, value, arguments.length > 1 );
6919 removeAttr: function( name ) {
6920 return this.each(function() {
6921 jQuery.removeAttr( this, name );
6927 attr: function( elem, name, value ) {
6929 nType = elem.nodeType;
6931 // don't get/set attributes on text, comment and attribute nodes
6932 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
6936 // Fallback to prop when attributes are not supported
6937 if ( typeof elem.getAttribute === strundefined ) {
6938 return jQuery.prop( elem, name, value );
6941 // All attributes are lowercase
6942 // Grab necessary hook if one is defined
6943 if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
6944 name = name.toLowerCase();
6945 hooks = jQuery.attrHooks[ name ] ||
6946 ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
6949 if ( value !== undefined ) {
6951 if ( value === null ) {
6952 jQuery.removeAttr( elem, name );
6954 } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
6958 elem.setAttribute( name, value + "" );
6962 } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
6966 ret = jQuery.find.attr( elem, name );
6968 // Non-existent attributes return null, we normalize to undefined
6969 return ret == null ?
6975 removeAttr: function( elem, value ) {
6978 attrNames = value && value.match( rnotwhite );
6980 if ( attrNames && elem.nodeType === 1 ) {
6981 while ( (name = attrNames[i++]) ) {
6982 propName = jQuery.propFix[ name ] || name;
6984 // Boolean attributes get special treatment (#10870)
6985 if ( jQuery.expr.match.bool.test( name ) ) {
6986 // Set corresponding property to false
6987 elem[ propName ] = false;
6990 elem.removeAttribute( name );
6997 set: function( elem, value ) {
6998 if ( !support.radioValue && value === "radio" &&
6999 jQuery.nodeName( elem, "input" ) ) {
7000 // Setting the type on a radio button after the value resets the value in IE6-9
7001 // Reset value to default in case type is set after value during creation
7002 var val = elem.value;
7003 elem.setAttribute( "type", value );
7014 // Hooks for boolean attributes
7016 set: function( elem, value, name ) {
7017 if ( value === false ) {
7018 // Remove boolean attributes when set to false
7019 jQuery.removeAttr( elem, name );
7021 elem.setAttribute( name, name );
7026 jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
7027 var getter = attrHandle[ name ] || jQuery.find.attr;
7029 attrHandle[ name ] = function( elem, name, isXML ) {
7032 // Avoid an infinite loop by temporarily removing this function from the getter
7033 handle = attrHandle[ name ];
7034 attrHandle[ name ] = ret;
7035 ret = getter( elem, name, isXML ) != null ?
7036 name.toLowerCase() :
7038 attrHandle[ name ] = handle;
7047 var rfocusable = /^(?:input|select|textarea|button)$/i;
7050 prop: function( name, value ) {
7051 return access( this, jQuery.prop, name, value, arguments.length > 1 );
7054 removeProp: function( name ) {
7055 return this.each(function() {
7056 delete this[ jQuery.propFix[ name ] || name ];
7064 "class": "className"
7067 prop: function( elem, name, value ) {
7068 var ret, hooks, notxml,
7069 nType = elem.nodeType;
7071 // don't get/set properties on text, comment and attribute nodes
7072 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
7076 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
7079 // Fix name and attach hooks
7080 name = jQuery.propFix[ name ] || name;
7081 hooks = jQuery.propHooks[ name ];
7084 if ( value !== undefined ) {
7085 return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
7087 ( elem[ name ] = value );
7090 return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
7098 get: function( elem ) {
7099 return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
7108 // Selectedness for an option in an optgroup can be inaccurate
7109 if ( !support.optSelected ) {
7110 jQuery.propHooks.selected = {
7111 get: function( elem ) {
7112 var parent = elem.parentNode;
7113 if ( parent && parent.parentNode ) {
7114 parent.parentNode.selectedIndex;
7133 jQuery.propFix[ this.toLowerCase() ] = this;
7139 var rclass = /[\t\r\n\f]/g;
7142 addClass: function( value ) {
7143 var classes, elem, cur, clazz, j, finalValue,
7144 proceed = typeof value === "string" && value,
7148 if ( jQuery.isFunction( value ) ) {
7149 return this.each(function( j ) {
7150 jQuery( this ).addClass( value.call( this, j, this.className ) );
7155 // The disjunction here is for better compressibility (see removeClass)
7156 classes = ( value || "" ).match( rnotwhite ) || [];
7158 for ( ; i < len; i++ ) {
7160 cur = elem.nodeType === 1 && ( elem.className ?
7161 ( " " + elem.className + " " ).replace( rclass, " " ) :
7167 while ( (clazz = classes[j++]) ) {
7168 if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
7173 // only assign if different to avoid unneeded rendering.
7174 finalValue = jQuery.trim( cur );
7175 if ( elem.className !== finalValue ) {
7176 elem.className = finalValue;
7185 removeClass: function( value ) {
7186 var classes, elem, cur, clazz, j, finalValue,
7187 proceed = arguments.length === 0 || typeof value === "string" && value,
7191 if ( jQuery.isFunction( value ) ) {
7192 return this.each(function( j ) {
7193 jQuery( this ).removeClass( value.call( this, j, this.className ) );
7197 classes = ( value || "" ).match( rnotwhite ) || [];
7199 for ( ; i < len; i++ ) {
7201 // This expression is here for better compressibility (see addClass)
7202 cur = elem.nodeType === 1 && ( elem.className ?
7203 ( " " + elem.className + " " ).replace( rclass, " " ) :
7209 while ( (clazz = classes[j++]) ) {
7210 // Remove *all* instances
7211 while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
7212 cur = cur.replace( " " + clazz + " ", " " );
7216 // only assign if different to avoid unneeded rendering.
7217 finalValue = value ? jQuery.trim( cur ) : "";
7218 if ( elem.className !== finalValue ) {
7219 elem.className = finalValue;
7228 toggleClass: function( value, stateVal ) {
7229 var type = typeof value;
7231 if ( typeof stateVal === "boolean" && type === "string" ) {
7232 return stateVal ? this.addClass( value ) : this.removeClass( value );
7235 if ( jQuery.isFunction( value ) ) {
7236 return this.each(function( i ) {
7237 jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
7241 return this.each(function() {
7242 if ( type === "string" ) {
7243 // toggle individual class names
7246 self = jQuery( this ),
7247 classNames = value.match( rnotwhite ) || [];
7249 while ( (className = classNames[ i++ ]) ) {
7250 // check each className given, space separated list
7251 if ( self.hasClass( className ) ) {
7252 self.removeClass( className );
7254 self.addClass( className );
7258 // Toggle whole class name
7259 } else if ( type === strundefined || type === "boolean" ) {
7260 if ( this.className ) {
7261 // store className if set
7262 data_priv.set( this, "__className__", this.className );
7265 // If the element has a class name or if we're passed "false",
7266 // then remove the whole classname (if there was one, the above saved it).
7267 // Otherwise bring back whatever was previously saved (if anything),
7268 // falling back to the empty string if nothing was stored.
7269 this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
7274 hasClass: function( selector ) {
7275 var className = " " + selector + " ",
7278 for ( ; i < l; i++ ) {
7279 if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
7291 var rreturn = /\r/g;
7294 val: function( value ) {
7295 var hooks, ret, isFunction,
7298 if ( !arguments.length ) {
7300 hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
7302 if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
7308 return typeof ret === "string" ?
7309 // handle most common string cases
7310 ret.replace(rreturn, "") :
7311 // handle cases where value is null/undef or number
7312 ret == null ? "" : ret;
7318 isFunction = jQuery.isFunction( value );
7320 return this.each(function( i ) {
7323 if ( this.nodeType !== 1 ) {
7328 val = value.call( this, i, jQuery( this ).val() );
7333 // Treat null/undefined as ""; convert numbers to string
7334 if ( val == null ) {
7337 } else if ( typeof val === "number" ) {
7340 } else if ( jQuery.isArray( val ) ) {
7341 val = jQuery.map( val, function( value ) {
7342 return value == null ? "" : value + "";
7346 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
7348 // If set returns undefined, fall back to normal setting
7349 if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
7359 get: function( elem ) {
7360 var val = jQuery.find.attr( elem, "value" );
7361 return val != null ?
7363 // Support: IE10-11+
7364 // option.text throws exceptions (#14686, #14858)
7365 jQuery.trim( jQuery.text( elem ) );
7369 get: function( elem ) {
7371 options = elem.options,
7372 index = elem.selectedIndex,
7373 one = elem.type === "select-one" || index < 0,
7374 values = one ? null : [],
7375 max = one ? index + 1 : options.length,
7380 // Loop through all the selected options
7381 for ( ; i < max; i++ ) {
7382 option = options[ i ];
7384 // IE6-9 doesn't update selected after form reset (#2551)
7385 if ( ( option.selected || i === index ) &&
7386 // Don't return options that are disabled or in a disabled optgroup
7387 ( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) &&
7388 ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
7390 // Get the specific value for the option
7391 value = jQuery( option ).val();
7393 // We don't need an array for one selects
7398 // Multi-Selects return an array
7399 values.push( value );
7406 set: function( elem, value ) {
7407 var optionSet, option,
7408 options = elem.options,
7409 values = jQuery.makeArray( value ),
7413 option = options[ i ];
7414 if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) {
7419 // force browsers to behave consistently when non-matching value is set
7421 elem.selectedIndex = -1;
7429 // Radios and checkboxes getter/setter
7430 jQuery.each([ "radio", "checkbox" ], function() {
7431 jQuery.valHooks[ this ] = {
7432 set: function( elem, value ) {
7433 if ( jQuery.isArray( value ) ) {
7434 return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
7438 if ( !support.checkOn ) {
7439 jQuery.valHooks[ this ].get = function( elem ) {
7441 // "" is returned instead of "on" if a value isn't specified
7442 return elem.getAttribute("value") === null ? "on" : elem.value;
7450 // Return jQuery for attributes-only inclusion
7453 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
7454 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
7455 "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
7457 // Handle event binding
7458 jQuery.fn[ name ] = function( data, fn ) {
7459 return arguments.length > 0 ?
7460 this.on( name, null, data, fn ) :
7461 this.trigger( name );
7466 hover: function( fnOver, fnOut ) {
7467 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
7470 bind: function( types, data, fn ) {
7471 return this.on( types, null, data, fn );
7473 unbind: function( types, fn ) {
7474 return this.off( types, null, fn );
7477 delegate: function( selector, types, data, fn ) {
7478 return this.on( types, selector, data, fn );
7480 undelegate: function( selector, types, fn ) {
7481 // ( namespace ) or ( selector, types [, fn] )
7482 return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
7487 var nonce = jQuery.now();
7489 var rquery = (/\?/);
7493 // Support: Android 2.3
7494 // Workaround failure to string-cast null input
7495 jQuery.parseJSON = function( data ) {
7496 return JSON.parse( data + "" );
7500 // Cross-browser xml parsing
7501 jQuery.parseXML = function( data ) {
7503 if ( !data || typeof data !== "string" ) {
7509 tmp = new DOMParser();
7510 xml = tmp.parseFromString( data, "text/xml" );
7515 if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
7516 jQuery.error( "Invalid XML: " + data );
7523 // Document location
7528 rts = /([?&])_=[^&]*/,
7529 rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
7530 // #7653, #8125, #8152: local protocol detection
7531 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
7532 rnoContent = /^(?:GET|HEAD)$/,
7533 rprotocol = /^\/\//,
7534 rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
7537 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
7538 * 2) These are called:
7539 * - BEFORE asking for a transport
7540 * - AFTER param serialization (s.data is a string if s.processData is true)
7541 * 3) key is the dataType
7542 * 4) the catchall symbol "*" can be used
7543 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
7547 /* Transports bindings
7548 * 1) key is the dataType
7549 * 2) the catchall symbol "*" can be used
7550 * 3) selection will start with transport dataType and THEN go to "*" if needed
7554 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
7555 allTypes = "*/".concat("*");
7557 // #8138, IE may throw an exception when accessing
7558 // a field from window.location if document.domain has been set
7560 ajaxLocation = location.href;
7562 // Use the href attribute of an A element
7563 // since IE will modify it given document.location
7564 ajaxLocation = document.createElement( "a" );
7565 ajaxLocation.href = "";
7566 ajaxLocation = ajaxLocation.href;
7569 // Segment location into parts
7570 ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
7572 // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
7573 function addToPrefiltersOrTransports( structure ) {
7575 // dataTypeExpression is optional and defaults to "*"
7576 return function( dataTypeExpression, func ) {
7578 if ( typeof dataTypeExpression !== "string" ) {
7579 func = dataTypeExpression;
7580 dataTypeExpression = "*";
7585 dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
7587 if ( jQuery.isFunction( func ) ) {
7588 // For each dataType in the dataTypeExpression
7589 while ( (dataType = dataTypes[i++]) ) {
7590 // Prepend if requested
7591 if ( dataType[0] === "+" ) {
7592 dataType = dataType.slice( 1 ) || "*";
7593 (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
7597 (structure[ dataType ] = structure[ dataType ] || []).push( func );
7604 // Base inspection function for prefilters and transports
7605 function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
7608 seekingTransport = ( structure === transports );
7610 function inspect( dataType ) {
7612 inspected[ dataType ] = true;
7613 jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
7614 var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
7615 if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
7616 options.dataTypes.unshift( dataTypeOrTransport );
7617 inspect( dataTypeOrTransport );
7619 } else if ( seekingTransport ) {
7620 return !( selected = dataTypeOrTransport );
7626 return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
7629 // A special extend for ajax options
7630 // that takes "flat" options (not to be deep extended)
7632 function ajaxExtend( target, src ) {
7634 flatOptions = jQuery.ajaxSettings.flatOptions || {};
7636 for ( key in src ) {
7637 if ( src[ key ] !== undefined ) {
7638 ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
7642 jQuery.extend( true, target, deep );
7648 /* Handles responses to an ajax request:
7649 * - finds the right dataType (mediates between content-type and expected dataType)
7650 * - returns the corresponding response
7652 function ajaxHandleResponses( s, jqXHR, responses ) {
7654 var ct, type, finalDataType, firstDataType,
7655 contents = s.contents,
7656 dataTypes = s.dataTypes;
7658 // Remove auto dataType and get content-type in the process
7659 while ( dataTypes[ 0 ] === "*" ) {
7661 if ( ct === undefined ) {
7662 ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
7666 // Check if we're dealing with a known content-type
7668 for ( type in contents ) {
7669 if ( contents[ type ] && contents[ type ].test( ct ) ) {
7670 dataTypes.unshift( type );
7676 // Check to see if we have a response for the expected dataType
7677 if ( dataTypes[ 0 ] in responses ) {
7678 finalDataType = dataTypes[ 0 ];
7680 // Try convertible dataTypes
7681 for ( type in responses ) {
7682 if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
7683 finalDataType = type;
7686 if ( !firstDataType ) {
7687 firstDataType = type;
7690 // Or just use first one
7691 finalDataType = finalDataType || firstDataType;
7694 // If we found a dataType
7695 // We add the dataType to the list if needed
7696 // and return the corresponding response
7697 if ( finalDataType ) {
7698 if ( finalDataType !== dataTypes[ 0 ] ) {
7699 dataTypes.unshift( finalDataType );
7701 return responses[ finalDataType ];
7705 /* Chain conversions given the request and the original response
7706 * Also sets the responseXXX fields on the jqXHR instance
7708 function ajaxConvert( s, response, jqXHR, isSuccess ) {
7709 var conv2, current, conv, tmp, prev,
7711 // Work with a copy of dataTypes in case we need to modify it for conversion
7712 dataTypes = s.dataTypes.slice();
7714 // Create converters map with lowercased keys
7715 if ( dataTypes[ 1 ] ) {
7716 for ( conv in s.converters ) {
7717 converters[ conv.toLowerCase() ] = s.converters[ conv ];
7721 current = dataTypes.shift();
7723 // Convert to each sequential dataType
7726 if ( s.responseFields[ current ] ) {
7727 jqXHR[ s.responseFields[ current ] ] = response;
7730 // Apply the dataFilter if provided
7731 if ( !prev && isSuccess && s.dataFilter ) {
7732 response = s.dataFilter( response, s.dataType );
7736 current = dataTypes.shift();
7740 // There's only work to do if current dataType is non-auto
7741 if ( current === "*" ) {
7745 // Convert response if prev dataType is non-auto and differs from current
7746 } else if ( prev !== "*" && prev !== current ) {
7748 // Seek a direct converter
7749 conv = converters[ prev + " " + current ] || converters[ "* " + current ];
7751 // If none found, seek a pair
7753 for ( conv2 in converters ) {
7755 // If conv2 outputs current
7756 tmp = conv2.split( " " );
7757 if ( tmp[ 1 ] === current ) {
7759 // If prev can be converted to accepted input
7760 conv = converters[ prev + " " + tmp[ 0 ] ] ||
7761 converters[ "* " + tmp[ 0 ] ];
7763 // Condense equivalence converters
7764 if ( conv === true ) {
7765 conv = converters[ conv2 ];
7767 // Otherwise, insert the intermediate dataType
7768 } else if ( converters[ conv2 ] !== true ) {
7770 dataTypes.unshift( tmp[ 1 ] );
7778 // Apply converter (if not an equivalence)
7779 if ( conv !== true ) {
7781 // Unless errors are allowed to bubble, catch and return them
7782 if ( conv && s[ "throws" ] ) {
7783 response = conv( response );
7786 response = conv( response );
7788 return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
7796 return { state: "success", data: response };
7801 // Counter for holding the number of active queries
7804 // Last-Modified header cache for next request
7811 isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
7815 contentType: "application/x-www-form-urlencoded; charset=UTF-8",
7832 xml: "application/xml, text/xml",
7833 json: "application/json, text/javascript"
7844 text: "responseText",
7845 json: "responseJSON"
7849 // Keys separate source (or catchall "*") and destination types with a single space
7852 // Convert anything to text
7855 // Text to html (true = no transformation)
7858 // Evaluate text as a json expression
7859 "text json": jQuery.parseJSON,
7861 // Parse text as xml
7862 "text xml": jQuery.parseXML
7865 // For options that shouldn't be deep extended:
7866 // you can add your own custom options here if
7867 // and when you create one that shouldn't be
7868 // deep extended (see ajaxExtend)
7875 // Creates a full fledged settings object into target
7876 // with both ajaxSettings and settings fields.
7877 // If target is omitted, writes into ajaxSettings.
7878 ajaxSetup: function( target, settings ) {
7881 // Building a settings object
7882 ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
7884 // Extending ajaxSettings
7885 ajaxExtend( jQuery.ajaxSettings, target );
7888 ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
7889 ajaxTransport: addToPrefiltersOrTransports( transports ),
7892 ajax: function( url, options ) {
7894 // If url is an object, simulate pre-1.5 signature
7895 if ( typeof url === "object" ) {
7900 // Force options to be an object
7901 options = options || {};
7904 // URL without anti-cache param
7907 responseHeadersString,
7911 // Cross-domain detection vars
7913 // To know if global events are to be dispatched
7917 // Create the final options object
7918 s = jQuery.ajaxSetup( {}, options ),
7919 // Callbacks context
7920 callbackContext = s.context || s,
7921 // Context for global events is callbackContext if it is a DOM node or jQuery collection
7922 globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
7923 jQuery( callbackContext ) :
7926 deferred = jQuery.Deferred(),
7927 completeDeferred = jQuery.Callbacks("once memory"),
7928 // Status-dependent callbacks
7929 statusCode = s.statusCode || {},
7930 // Headers (they are sent all at once)
7931 requestHeaders = {},
7932 requestHeadersNames = {},
7935 // Default abort message
7936 strAbort = "canceled",
7941 // Builds headers hashtable if needed
7942 getResponseHeader: function( key ) {
7944 if ( state === 2 ) {
7945 if ( !responseHeaders ) {
7946 responseHeaders = {};
7947 while ( (match = rheaders.exec( responseHeadersString )) ) {
7948 responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
7951 match = responseHeaders[ key.toLowerCase() ];
7953 return match == null ? null : match;
7957 getAllResponseHeaders: function() {
7958 return state === 2 ? responseHeadersString : null;
7961 // Caches the header
7962 setRequestHeader: function( name, value ) {
7963 var lname = name.toLowerCase();
7965 name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
7966 requestHeaders[ name ] = value;
7971 // Overrides response content-type header
7972 overrideMimeType: function( type ) {
7979 // Status-dependent callbacks
7980 statusCode: function( map ) {
7984 for ( code in map ) {
7985 // Lazy-add the new callback in a way that preserves old ones
7986 statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
7989 // Execute the appropriate callbacks
7990 jqXHR.always( map[ jqXHR.status ] );
7996 // Cancel the request
7997 abort: function( statusText ) {
7998 var finalText = statusText || strAbort;
8000 transport.abort( finalText );
8002 done( 0, finalText );
8008 deferred.promise( jqXHR ).complete = completeDeferred.add;
8009 jqXHR.success = jqXHR.done;
8010 jqXHR.error = jqXHR.fail;
8012 // Remove hash character (#7531: and string promotion)
8013 // Add protocol if not provided (prefilters might expect it)
8014 // Handle falsy url in the settings object (#10093: consistency with old signature)
8015 // We also use the url parameter if available
8016 s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
8017 .replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
8019 // Alias method option to type as per ticket #12004
8020 s.type = options.method || options.type || s.method || s.type;
8022 // Extract dataTypes list
8023 s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
8025 // A cross-domain request is in order when we have a protocol:host:port mismatch
8026 if ( s.crossDomain == null ) {
8027 parts = rurl.exec( s.url.toLowerCase() );
8028 s.crossDomain = !!( parts &&
8029 ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
8030 ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
8031 ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
8035 // Convert data if not already a string
8036 if ( s.data && s.processData && typeof s.data !== "string" ) {
8037 s.data = jQuery.param( s.data, s.traditional );
8041 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
8043 // If request was aborted inside a prefilter, stop there
8044 if ( state === 2 ) {
8048 // We can fire global events as of now if asked to
8049 fireGlobals = s.global;
8051 // Watch for a new set of requests
8052 if ( fireGlobals && jQuery.active++ === 0 ) {
8053 jQuery.event.trigger("ajaxStart");
8056 // Uppercase the type
8057 s.type = s.type.toUpperCase();
8059 // Determine if request has content
8060 s.hasContent = !rnoContent.test( s.type );
8062 // Save the URL in case we're toying with the If-Modified-Since
8063 // and/or If-None-Match header later on
8066 // More options handling for requests with no content
8067 if ( !s.hasContent ) {
8069 // If data is available, append data to url
8071 cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
8072 // #9682: remove data so that it's not used in an eventual retry
8076 // Add anti-cache in url if needed
8077 if ( s.cache === false ) {
8078 s.url = rts.test( cacheURL ) ?
8080 // If there is already a '_' parameter, set its value
8081 cacheURL.replace( rts, "$1_=" + nonce++ ) :
8083 // Otherwise add one to the end
8084 cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
8088 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8089 if ( s.ifModified ) {
8090 if ( jQuery.lastModified[ cacheURL ] ) {
8091 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
8093 if ( jQuery.etag[ cacheURL ] ) {
8094 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
8098 // Set the correct header, if data is being sent
8099 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
8100 jqXHR.setRequestHeader( "Content-Type", s.contentType );
8103 // Set the Accepts header for the server, depending on the dataType
8104 jqXHR.setRequestHeader(
8106 s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
8107 s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
8111 // Check for headers option
8112 for ( i in s.headers ) {
8113 jqXHR.setRequestHeader( i, s.headers[ i ] );
8116 // Allow custom headers/mimetypes and early abort
8117 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
8118 // Abort if not done already and return
8119 return jqXHR.abort();
8122 // aborting is no longer a cancellation
8125 // Install callbacks on deferreds
8126 for ( i in { success: 1, error: 1, complete: 1 } ) {
8127 jqXHR[ i ]( s[ i ] );
8131 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
8133 // If no transport, we auto-abort
8135 done( -1, "No Transport" );
8137 jqXHR.readyState = 1;
8139 // Send global event
8140 if ( fireGlobals ) {
8141 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
8144 if ( s.async && s.timeout > 0 ) {
8145 timeoutTimer = setTimeout(function() {
8146 jqXHR.abort("timeout");
8152 transport.send( requestHeaders, done );
8154 // Propagate exception as error if not done
8157 // Simply rethrow otherwise
8164 // Callback for when everything is done
8165 function done( status, nativeStatusText, responses, headers ) {
8166 var isSuccess, success, error, response, modified,
8167 statusText = nativeStatusText;
8170 if ( state === 2 ) {
8174 // State is "done" now
8177 // Clear timeout if it exists
8178 if ( timeoutTimer ) {
8179 clearTimeout( timeoutTimer );
8182 // Dereference transport for early garbage collection
8183 // (no matter how long the jqXHR object will be used)
8184 transport = undefined;
8186 // Cache response headers
8187 responseHeadersString = headers || "";
8190 jqXHR.readyState = status > 0 ? 4 : 0;
8192 // Determine if successful
8193 isSuccess = status >= 200 && status < 300 || status === 304;
8195 // Get response data
8197 response = ajaxHandleResponses( s, jqXHR, responses );
8200 // Convert no matter what (that way responseXXX fields are always set)
8201 response = ajaxConvert( s, response, jqXHR, isSuccess );
8203 // If successful, handle type chaining
8206 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8207 if ( s.ifModified ) {
8208 modified = jqXHR.getResponseHeader("Last-Modified");
8210 jQuery.lastModified[ cacheURL ] = modified;
8212 modified = jqXHR.getResponseHeader("etag");
8214 jQuery.etag[ cacheURL ] = modified;
8219 if ( status === 204 || s.type === "HEAD" ) {
8220 statusText = "nocontent";
8223 } else if ( status === 304 ) {
8224 statusText = "notmodified";
8226 // If we have data, let's convert it
8228 statusText = response.state;
8229 success = response.data;
8230 error = response.error;
8234 // We extract error from statusText
8235 // then normalize statusText and status for non-aborts
8237 if ( status || !statusText ) {
8238 statusText = "error";
8245 // Set data for the fake xhr object
8246 jqXHR.status = status;
8247 jqXHR.statusText = ( nativeStatusText || statusText ) + "";
8251 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
8253 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
8256 // Status-dependent callbacks
8257 jqXHR.statusCode( statusCode );
8258 statusCode = undefined;
8260 if ( fireGlobals ) {
8261 globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
8262 [ jqXHR, s, isSuccess ? success : error ] );
8266 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
8268 if ( fireGlobals ) {
8269 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
8270 // Handle the global AJAX counter
8271 if ( !( --jQuery.active ) ) {
8272 jQuery.event.trigger("ajaxStop");
8280 getJSON: function( url, data, callback ) {
8281 return jQuery.get( url, data, callback, "json" );
8284 getScript: function( url, callback ) {
8285 return jQuery.get( url, undefined, callback, "script" );
8289 jQuery.each( [ "get", "post" ], function( i, method ) {
8290 jQuery[ method ] = function( url, data, callback, type ) {
8291 // shift arguments if data argument was omitted
8292 if ( jQuery.isFunction( data ) ) {
8293 type = type || callback;
8298 return jQuery.ajax({
8308 // Attach a bunch of functions for handling common AJAX events
8309 jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
8310 jQuery.fn[ type ] = function( fn ) {
8311 return this.on( type, fn );
8316 jQuery._evalUrl = function( url ) {
8317 return jQuery.ajax({
8329 wrapAll: function( html ) {
8332 if ( jQuery.isFunction( html ) ) {
8333 return this.each(function( i ) {
8334 jQuery( this ).wrapAll( html.call(this, i) );
8340 // The elements to wrap the target around
8341 wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
8343 if ( this[ 0 ].parentNode ) {
8344 wrap.insertBefore( this[ 0 ] );
8347 wrap.map(function() {
8350 while ( elem.firstElementChild ) {
8351 elem = elem.firstElementChild;
8361 wrapInner: function( html ) {
8362 if ( jQuery.isFunction( html ) ) {
8363 return this.each(function( i ) {
8364 jQuery( this ).wrapInner( html.call(this, i) );
8368 return this.each(function() {
8369 var self = jQuery( this ),
8370 contents = self.contents();
8372 if ( contents.length ) {
8373 contents.wrapAll( html );
8376 self.append( html );
8381 wrap: function( html ) {
8382 var isFunction = jQuery.isFunction( html );
8384 return this.each(function( i ) {
8385 jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
8389 unwrap: function() {
8390 return this.parent().each(function() {
8391 if ( !jQuery.nodeName( this, "body" ) ) {
8392 jQuery( this ).replaceWith( this.childNodes );
8399 jQuery.expr.filters.hidden = function( elem ) {
8400 // Support: Opera <= 12.12
8401 // Opera reports offsetWidths and offsetHeights less than zero on some elements
8402 return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
8404 jQuery.expr.filters.visible = function( elem ) {
8405 return !jQuery.expr.filters.hidden( elem );
8414 rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
8415 rsubmittable = /^(?:input|select|textarea|keygen)/i;
8417 function buildParams( prefix, obj, traditional, add ) {
8420 if ( jQuery.isArray( obj ) ) {
8421 // Serialize array item.
8422 jQuery.each( obj, function( i, v ) {
8423 if ( traditional || rbracket.test( prefix ) ) {
8424 // Treat each array item as a scalar.
8428 // Item is non-scalar (array or object), encode its numeric index.
8429 buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
8433 } else if ( !traditional && jQuery.type( obj ) === "object" ) {
8434 // Serialize object item.
8435 for ( name in obj ) {
8436 buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
8440 // Serialize scalar item.
8445 // Serialize an array of form elements or a set of
8446 // key/values into a query string
8447 jQuery.param = function( a, traditional ) {
8450 add = function( key, value ) {
8451 // If value is a function, invoke it and return its value
8452 value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
8453 s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
8456 // Set traditional to true for jQuery <= 1.3.2 behavior.
8457 if ( traditional === undefined ) {
8458 traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
8461 // If an array was passed in, assume that it is an array of form elements.
8462 if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
8463 // Serialize the form elements
8464 jQuery.each( a, function() {
8465 add( this.name, this.value );
8469 // If traditional, encode the "old" way (the way 1.3.2 or older
8470 // did it), otherwise encode params recursively.
8471 for ( prefix in a ) {
8472 buildParams( prefix, a[ prefix ], traditional, add );
8476 // Return the resulting serialization
8477 return s.join( "&" ).replace( r20, "+" );
8481 serialize: function() {
8482 return jQuery.param( this.serializeArray() );
8484 serializeArray: function() {
8485 return this.map(function() {
8486 // Can add propHook for "elements" to filter or add form elements
8487 var elements = jQuery.prop( this, "elements" );
8488 return elements ? jQuery.makeArray( elements ) : this;
8490 .filter(function() {
8491 var type = this.type;
8493 // Use .is( ":disabled" ) so that fieldset[disabled] works
8494 return this.name && !jQuery( this ).is( ":disabled" ) &&
8495 rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
8496 ( this.checked || !rcheckableType.test( type ) );
8498 .map(function( i, elem ) {
8499 var val = jQuery( this ).val();
8501 return val == null ?
8503 jQuery.isArray( val ) ?
8504 jQuery.map( val, function( val ) {
8505 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
8507 { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
8513 jQuery.ajaxSettings.xhr = function() {
8515 return new XMLHttpRequest();
8521 xhrSuccessStatus = {
8522 // file protocol always yields status code 0, assume 200
8525 // #1450: sometimes IE returns 1223 when it should be 204
8528 xhrSupported = jQuery.ajaxSettings.xhr();
8531 // Open requests must be manually aborted on unload (#5280)
8532 if ( window.ActiveXObject ) {
8533 jQuery( window ).on( "unload", function() {
8534 for ( var key in xhrCallbacks ) {
8535 xhrCallbacks[ key ]();
8540 support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
8541 support.ajax = xhrSupported = !!xhrSupported;
8543 jQuery.ajaxTransport(function( options ) {
8546 // Cross domain only allowed if supported through XMLHttpRequest
8547 if ( support.cors || xhrSupported && !options.crossDomain ) {
8549 send: function( headers, complete ) {
8551 xhr = options.xhr(),
8554 xhr.open( options.type, options.url, options.async, options.username, options.password );
8556 // Apply custom fields if provided
8557 if ( options.xhrFields ) {
8558 for ( i in options.xhrFields ) {
8559 xhr[ i ] = options.xhrFields[ i ];
8563 // Override mime type if needed
8564 if ( options.mimeType && xhr.overrideMimeType ) {
8565 xhr.overrideMimeType( options.mimeType );
8568 // X-Requested-With header
8569 // For cross-domain requests, seeing as conditions for a preflight are
8570 // akin to a jigsaw puzzle, we simply never set it to be sure.
8571 // (it can always be set on a per-request basis or even using ajaxSetup)
8572 // For same-domain requests, won't change header if already provided.
8573 if ( !options.crossDomain && !headers["X-Requested-With"] ) {
8574 headers["X-Requested-With"] = "XMLHttpRequest";
8578 for ( i in headers ) {
8579 xhr.setRequestHeader( i, headers[ i ] );
8583 callback = function( type ) {
8586 delete xhrCallbacks[ id ];
8587 callback = xhr.onload = xhr.onerror = null;
8589 if ( type === "abort" ) {
8591 } else if ( type === "error" ) {
8593 // file: protocol always yields status 0; see #8605, #14207
8599 xhrSuccessStatus[ xhr.status ] || xhr.status,
8602 // Accessing binary-data responseText throws an exception
8604 typeof xhr.responseText === "string" ? {
8605 text: xhr.responseText
8607 xhr.getAllResponseHeaders()
8615 xhr.onload = callback();
8616 xhr.onerror = callback("error");
8618 // Create the abort callback
8619 callback = xhrCallbacks[ id ] = callback("abort");
8622 // Do send the request (this may raise an exception)
8623 xhr.send( options.hasContent && options.data || null );
8625 // #14683: Only rethrow if this hasn't been notified as an error yet
8644 // Install script dataType
8647 script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
8650 script: /(?:java|ecma)script/
8653 "text script": function( text ) {
8654 jQuery.globalEval( text );
8660 // Handle cache's special case and crossDomain
8661 jQuery.ajaxPrefilter( "script", function( s ) {
8662 if ( s.cache === undefined ) {
8665 if ( s.crossDomain ) {
8670 // Bind script tag hack transport
8671 jQuery.ajaxTransport( "script", function( s ) {
8672 // This transport only deals with cross domain requests
8673 if ( s.crossDomain ) {
8674 var script, callback;
8676 send: function( _, complete ) {
8677 script = jQuery("<script>").prop({
8679 charset: s.scriptCharset,
8683 callback = function( evt ) {
8687 complete( evt.type === "error" ? 404 : 200, evt.type );
8691 document.head.appendChild( script[ 0 ] );
8705 var oldCallbacks = [],
8706 rjsonp = /(=)\?(?=&|$)|\?\?/;
8708 // Default jsonp settings
8711 jsonpCallback: function() {
8712 var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
8713 this[ callback ] = true;
8718 // Detect, normalize options and install callbacks for jsonp requests
8719 jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
8721 var callbackName, overwritten, responseContainer,
8722 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
8724 typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
8727 // Handle iff the expected data type is "jsonp" or we have a parameter to set
8728 if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
8730 // Get callback name, remembering preexisting value associated with it
8731 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
8735 // Insert callback into url or form data
8737 s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
8738 } else if ( s.jsonp !== false ) {
8739 s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
8742 // Use data converter to retrieve json after script execution
8743 s.converters["script json"] = function() {
8744 if ( !responseContainer ) {
8745 jQuery.error( callbackName + " was not called" );
8747 return responseContainer[ 0 ];
8750 // force json dataType
8751 s.dataTypes[ 0 ] = "json";
8754 overwritten = window[ callbackName ];
8755 window[ callbackName ] = function() {
8756 responseContainer = arguments;
8759 // Clean-up function (fires after converters)
8760 jqXHR.always(function() {
8761 // Restore preexisting value
8762 window[ callbackName ] = overwritten;
8764 // Save back as free
8765 if ( s[ callbackName ] ) {
8766 // make sure that re-using the options doesn't screw things around
8767 s.jsonpCallback = originalSettings.jsonpCallback;
8769 // save the callback name for future use
8770 oldCallbacks.push( callbackName );
8773 // Call if it was a function and we have a response
8774 if ( responseContainer && jQuery.isFunction( overwritten ) ) {
8775 overwritten( responseContainer[ 0 ] );
8778 responseContainer = overwritten = undefined;
8781 // Delegate to script
8789 // data: string of html
8790 // context (optional): If specified, the fragment will be created in this context, defaults to document
8791 // keepScripts (optional): If true, will include scripts passed in the html string
8792 jQuery.parseHTML = function( data, context, keepScripts ) {
8793 if ( !data || typeof data !== "string" ) {
8796 if ( typeof context === "boolean" ) {
8797 keepScripts = context;
8800 context = context || document;
8802 var parsed = rsingleTag.exec( data ),
8803 scripts = !keepScripts && [];
8807 return [ context.createElement( parsed[1] ) ];
8810 parsed = jQuery.buildFragment( [ data ], context, scripts );
8812 if ( scripts && scripts.length ) {
8813 jQuery( scripts ).remove();
8816 return jQuery.merge( [], parsed.childNodes );
8820 // Keep a copy of the old load method
8821 var _load = jQuery.fn.load;
8824 * Load a url into a page
8826 jQuery.fn.load = function( url, params, callback ) {
8827 if ( typeof url !== "string" && _load ) {
8828 return _load.apply( this, arguments );
8831 var selector, type, response,
8833 off = url.indexOf(" ");
8836 selector = jQuery.trim( url.slice( off ) );
8837 url = url.slice( 0, off );
8840 // If it's a function
8841 if ( jQuery.isFunction( params ) ) {
8843 // We assume that it's the callback
8847 // Otherwise, build a param string
8848 } else if ( params && typeof params === "object" ) {
8852 // If we have elements to modify, make the request
8853 if ( self.length > 0 ) {
8857 // if "type" variable is undefined, then "GET" method will be used
8861 }).done(function( responseText ) {
8863 // Save response for use in complete callback
8864 response = arguments;
8866 self.html( selector ?
8868 // If a selector was specified, locate the right elements in a dummy div
8869 // Exclude scripts to avoid IE 'Permission Denied' errors
8870 jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
8872 // Otherwise use the full result
8875 }).complete( callback && function( jqXHR, status ) {
8876 self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
8886 jQuery.expr.filters.animated = function( elem ) {
8887 return jQuery.grep(jQuery.timers, function( fn ) {
8888 return elem === fn.elem;
8895 var docElem = window.document.documentElement;
8898 * Gets a window from an element
8900 function getWindow( elem ) {
8901 return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
8905 setOffset: function( elem, options, i ) {
8906 var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
8907 position = jQuery.css( elem, "position" ),
8908 curElem = jQuery( elem ),
8911 // Set position first, in-case top/left are set even on static elem
8912 if ( position === "static" ) {
8913 elem.style.position = "relative";
8916 curOffset = curElem.offset();
8917 curCSSTop = jQuery.css( elem, "top" );
8918 curCSSLeft = jQuery.css( elem, "left" );
8919 calculatePosition = ( position === "absolute" || position === "fixed" ) &&
8920 ( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
8922 // Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
8923 if ( calculatePosition ) {
8924 curPosition = curElem.position();
8925 curTop = curPosition.top;
8926 curLeft = curPosition.left;
8929 curTop = parseFloat( curCSSTop ) || 0;
8930 curLeft = parseFloat( curCSSLeft ) || 0;
8933 if ( jQuery.isFunction( options ) ) {
8934 options = options.call( elem, i, curOffset );
8937 if ( options.top != null ) {
8938 props.top = ( options.top - curOffset.top ) + curTop;
8940 if ( options.left != null ) {
8941 props.left = ( options.left - curOffset.left ) + curLeft;
8944 if ( "using" in options ) {
8945 options.using.call( elem, props );
8948 curElem.css( props );
8954 offset: function( options ) {
8955 if ( arguments.length ) {
8956 return options === undefined ?
8958 this.each(function( i ) {
8959 jQuery.offset.setOffset( this, options, i );
8965 box = { top: 0, left: 0 },
8966 doc = elem && elem.ownerDocument;
8972 docElem = doc.documentElement;
8974 // Make sure it's not a disconnected DOM node
8975 if ( !jQuery.contains( docElem, elem ) ) {
8979 // If we don't have gBCR, just use 0,0 rather than error
8980 // BlackBerry 5, iOS 3 (original iPhone)
8981 if ( typeof elem.getBoundingClientRect !== strundefined ) {
8982 box = elem.getBoundingClientRect();
8984 win = getWindow( doc );
8986 top: box.top + win.pageYOffset - docElem.clientTop,
8987 left: box.left + win.pageXOffset - docElem.clientLeft
8991 position: function() {
8996 var offsetParent, offset,
8998 parentOffset = { top: 0, left: 0 };
9000 // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
9001 if ( jQuery.css( elem, "position" ) === "fixed" ) {
9002 // We assume that getBoundingClientRect is available when computed position is fixed
9003 offset = elem.getBoundingClientRect();
9006 // Get *real* offsetParent
9007 offsetParent = this.offsetParent();
9009 // Get correct offsets
9010 offset = this.offset();
9011 if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
9012 parentOffset = offsetParent.offset();
9015 // Add offsetParent borders
9016 parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
9017 parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
9020 // Subtract parent offsets and element margins
9022 top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
9023 left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
9027 offsetParent: function() {
9028 return this.map(function() {
9029 var offsetParent = this.offsetParent || docElem;
9031 while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
9032 offsetParent = offsetParent.offsetParent;
9035 return offsetParent || docElem;
9040 // Create scrollLeft and scrollTop methods
9041 jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
9042 var top = "pageYOffset" === prop;
9044 jQuery.fn[ method ] = function( val ) {
9045 return access( this, function( elem, method, val ) {
9046 var win = getWindow( elem );
9048 if ( val === undefined ) {
9049 return win ? win[ prop ] : elem[ method ];
9054 !top ? val : window.pageXOffset,
9055 top ? val : window.pageYOffset
9059 elem[ method ] = val;
9061 }, method, val, arguments.length, null );
9065 // Add the top/left cssHooks using jQuery.fn.position
9066 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
9067 // getComputedStyle returns percent when specified for top/left/bottom/right
9068 // rather than make the css module depend on the offset module, we just check for it here
9069 jQuery.each( [ "top", "left" ], function( i, prop ) {
9070 jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
9071 function( elem, computed ) {
9073 computed = curCSS( elem, prop );
9074 // if curCSS returns percentage, fallback to offset
9075 return rnumnonpx.test( computed ) ?
9076 jQuery( elem ).position()[ prop ] + "px" :
9084 // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
9085 jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
9086 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
9087 // margin is only for outerHeight, outerWidth
9088 jQuery.fn[ funcName ] = function( margin, value ) {
9089 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
9090 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
9092 return access( this, function( elem, type, value ) {
9095 if ( jQuery.isWindow( elem ) ) {
9096 // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
9097 // isn't a whole lot we can do. See pull request at this URL for discussion:
9098 // https://github.com/jquery/jquery/pull/764
9099 return elem.document.documentElement[ "client" + name ];
9102 // Get document width or height
9103 if ( elem.nodeType === 9 ) {
9104 doc = elem.documentElement;
9106 // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
9107 // whichever is greatest
9109 elem.body[ "scroll" + name ], doc[ "scroll" + name ],
9110 elem.body[ "offset" + name ], doc[ "offset" + name ],
9111 doc[ "client" + name ]
9115 return value === undefined ?
9116 // Get width or height on the element, requesting but not forcing parseFloat
9117 jQuery.css( elem, type, extra ) :
9119 // Set width or height on the element
9120 jQuery.style( elem, type, value, extra );
9121 }, type, chainable ? margin : undefined, chainable, null );
9127 // The number of elements contained in the matched element set
9128 jQuery.fn.size = function() {
9132 jQuery.fn.andSelf = jQuery.fn.addBack;
9137 // Register as a named AMD module, since jQuery can be concatenated with other
9138 // files that may use define, but not via a proper concatenation script that
9139 // understands anonymous AMD modules. A named AMD is safest and most robust
9140 // way to register. Lowercase jquery is used because AMD module names are
9141 // derived from file names, and jQuery is normally delivered in a lowercase
9142 // file name. Do this after creating the global so that if an AMD module wants
9143 // to call noConflict to hide this version of jQuery, it will work.
9145 // Note that for maximum portability, libraries that are not jQuery should
9146 // declare themselves as anonymous modules, and avoid setting a global if an
9147 // AMD loader is present. jQuery is a special case. For more information, see
9148 // https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
9150 if ( typeof define === "function" && define.amd ) {
9151 define( "jquery", [], function() {
9160 // Map over jQuery in case of overwrite
9161 _jQuery = window.jQuery,
9163 // Map over the $ in case of overwrite
9166 jQuery.noConflict = function( deep ) {
9167 if ( window.$ === jQuery ) {
9171 if ( deep && window.jQuery === jQuery ) {
9172 window.jQuery = _jQuery;
9178 // Expose jQuery and $ identifiers, even in
9179 // AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
9180 // and CommonJS for browser emulators (#13566)
9181 if ( typeof noGlobal === strundefined ) {
9182 window.jQuery = window.$ = jQuery;
9193 * @license AngularJS v1.5.0
9194 * (c) 2010-2016 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.5.0/' +
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';
9383 var hasOwnProperty = Object.prototype.hasOwnProperty;
9385 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
9386 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
9389 var manualLowercase = function(s) {
9390 /* jshint bitwise: false */
9392 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
9395 var manualUppercase = function(s) {
9396 /* jshint bitwise: false */
9398 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
9403 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
9404 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
9405 // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
9406 if ('i' !== 'I'.toLowerCase()) {
9407 lowercase = manualLowercase;
9408 uppercase = manualUppercase;
9413 msie, // holds major version number for IE, or NaN if UA is not IE.
9414 jqLite, // delay binding since jQuery could be loaded after us.
9415 jQuery, // delay binding
9419 toString = Object.prototype.toString,
9420 getPrototypeOf = Object.getPrototypeOf,
9421 ngMinErr = minErr('ng'),
9423 /** @name angular */
9424 angular = window.angular || (window.angular = {}),
9429 * documentMode is an IE-only property
9430 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
9432 msie = document.documentMode;
9438 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
9441 function isArrayLike(obj) {
9443 // `null`, `undefined` and `window` are not array-like
9444 if (obj == null || isWindow(obj)) return false;
9446 // arrays, strings and jQuery/jqLite objects are array like
9447 // * jqLite is either the jQuery or jqLite constructor function
9448 // * we have to check the existence of jqLite first as this method is called
9449 // via the forEach method when constructing the jqLite object in the first place
9450 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
9452 // Support: iOS 8.2 (not reproducible in simulator)
9453 // "length" in obj used to prevent JIT error (gh-11508)
9454 var length = "length" in Object(obj) && obj.length;
9456 // NodeList objects (with `item` method) and
9457 // other objects with suitable length characteristics are array-like
9458 return isNumber(length) &&
9459 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
9465 * @name angular.forEach
9470 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
9471 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
9472 * is the value of an object property or an array element, `key` is the object property key or
9473 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
9475 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
9476 * using the `hasOwnProperty` method.
9479 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
9480 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
9481 * return the value provided.
9484 var values = {name: 'misko', gender: 'male'};
9486 angular.forEach(values, function(value, key) {
9487 this.push(key + ': ' + value);
9489 expect(log).toEqual(['name: misko', 'gender: male']);
9492 * @param {Object|Array} obj Object to iterate over.
9493 * @param {Function} iterator Iterator function.
9494 * @param {Object=} context Object to become context (`this`) for the iterator function.
9495 * @returns {Object|Array} Reference to `obj`.
9498 function forEach(obj, iterator, context) {
9501 if (isFunction(obj)) {
9503 // Need to check if hasOwnProperty exists,
9504 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
9505 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
9506 iterator.call(context, obj[key], key, obj);
9509 } else if (isArray(obj) || isArrayLike(obj)) {
9510 var isPrimitive = typeof obj !== 'object';
9511 for (key = 0, length = obj.length; key < length; key++) {
9512 if (isPrimitive || key in obj) {
9513 iterator.call(context, obj[key], key, obj);
9516 } else if (obj.forEach && obj.forEach !== forEach) {
9517 obj.forEach(iterator, context, obj);
9518 } else if (isBlankObject(obj)) {
9519 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
9521 iterator.call(context, obj[key], key, obj);
9523 } else if (typeof obj.hasOwnProperty === 'function') {
9524 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
9526 if (obj.hasOwnProperty(key)) {
9527 iterator.call(context, obj[key], key, obj);
9531 // Slow path for objects which do not have a method `hasOwnProperty`
9533 if (hasOwnProperty.call(obj, key)) {
9534 iterator.call(context, obj[key], key, obj);
9542 function forEachSorted(obj, iterator, context) {
9543 var keys = Object.keys(obj).sort();
9544 for (var i = 0; i < keys.length; i++) {
9545 iterator.call(context, obj[keys[i]], keys[i]);
9552 * when using forEach the params are value, key, but it is often useful to have key, value.
9553 * @param {function(string, *)} iteratorFn
9554 * @returns {function(*, string)}
9556 function reverseParams(iteratorFn) {
9557 return function(value, key) {iteratorFn(key, value);};
9561 * A consistent way of creating unique IDs in angular.
9563 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
9564 * we hit number precision issues in JavaScript.
9566 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
9568 * @returns {number} an unique alpha-numeric string
9570 function nextUid() {
9576 * Set or clear the hashkey for an object.
9578 * @param h the hashkey (!truthy to delete the hashkey)
9580 function setHashKey(obj, h) {
9584 delete obj.$$hashKey;
9589 function baseExtend(dst, objs, deep) {
9590 var h = dst.$$hashKey;
9592 for (var i = 0, ii = objs.length; i < ii; ++i) {
9594 if (!isObject(obj) && !isFunction(obj)) continue;
9595 var keys = Object.keys(obj);
9596 for (var j = 0, jj = keys.length; j < jj; j++) {
9600 if (deep && isObject(src)) {
9602 dst[key] = new Date(src.valueOf());
9603 } else if (isRegExp(src)) {
9604 dst[key] = new RegExp(src);
9605 } else if (src.nodeName) {
9606 dst[key] = src.cloneNode(true);
9607 } else if (isElement(src)) {
9608 dst[key] = src.clone();
9610 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
9611 baseExtend(dst[key], [src], true);
9625 * @name angular.extend
9630 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
9631 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
9632 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
9634 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
9635 * {@link angular.merge} for this.
9637 * @param {Object} dst Destination object.
9638 * @param {...Object} src Source object(s).
9639 * @returns {Object} Reference to `dst`.
9641 function extend(dst) {
9642 return baseExtend(dst, slice.call(arguments, 1), false);
9648 * @name angular.merge
9653 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
9654 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
9655 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
9657 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
9658 * objects, performing a deep copy.
9660 * @param {Object} dst Destination object.
9661 * @param {...Object} src Source object(s).
9662 * @returns {Object} Reference to `dst`.
9664 function merge(dst) {
9665 return baseExtend(dst, slice.call(arguments, 1), true);
9670 function toInt(str) {
9671 return parseInt(str, 10);
9675 function inherit(parent, extra) {
9676 return extend(Object.create(parent), extra);
9681 * @name angular.noop
9686 * A function that performs no operations. This function can be useful when writing code in the
9689 function foo(callback) {
9690 var result = calculateResult();
9691 (callback || angular.noop)(result);
9701 * @name angular.identity
9706 * A function that returns its first argument. This function is useful when writing code in the
9710 function transformer(transformationFn, value) {
9711 return (transformationFn || angular.identity)(value);
9714 * @param {*} value to be returned.
9715 * @returns {*} the value passed in.
9717 function identity($) {return $;}
9718 identity.$inject = [];
9721 function valueFn(value) {return function() {return value;};}
9723 function hasCustomToString(obj) {
9724 return isFunction(obj.toString) && obj.toString !== toString;
9730 * @name angular.isUndefined
9735 * Determines if a reference is undefined.
9737 * @param {*} value Reference to check.
9738 * @returns {boolean} True if `value` is undefined.
9740 function isUndefined(value) {return typeof value === 'undefined';}
9745 * @name angular.isDefined
9750 * Determines if a reference is defined.
9752 * @param {*} value Reference to check.
9753 * @returns {boolean} True if `value` is defined.
9755 function isDefined(value) {return typeof value !== 'undefined';}
9760 * @name angular.isObject
9765 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
9766 * considered to be objects. Note that JavaScript arrays are objects.
9768 * @param {*} value Reference to check.
9769 * @returns {boolean} True if `value` is an `Object` but not `null`.
9771 function isObject(value) {
9772 // http://jsperf.com/isobject4
9773 return value !== null && typeof value === 'object';
9778 * Determine if a value is an object with a null prototype
9780 * @returns {boolean} True if `value` is an `Object` with a null prototype
9782 function isBlankObject(value) {
9783 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
9789 * @name angular.isString
9794 * Determines if a reference is a `String`.
9796 * @param {*} value Reference to check.
9797 * @returns {boolean} True if `value` is a `String`.
9799 function isString(value) {return typeof value === 'string';}
9804 * @name angular.isNumber
9809 * Determines if a reference is a `Number`.
9811 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
9813 * If you wish to exclude these then you can use the native
9814 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
9817 * @param {*} value Reference to check.
9818 * @returns {boolean} True if `value` is a `Number`.
9820 function isNumber(value) {return typeof value === 'number';}
9825 * @name angular.isDate
9830 * Determines if a value is a date.
9832 * @param {*} value Reference to check.
9833 * @returns {boolean} True if `value` is a `Date`.
9835 function isDate(value) {
9836 return toString.call(value) === '[object Date]';
9842 * @name angular.isArray
9847 * Determines if a reference is an `Array`.
9849 * @param {*} value Reference to check.
9850 * @returns {boolean} True if `value` is an `Array`.
9852 var isArray = Array.isArray;
9856 * @name angular.isFunction
9861 * Determines if a reference is a `Function`.
9863 * @param {*} value Reference to check.
9864 * @returns {boolean} True if `value` is a `Function`.
9866 function isFunction(value) {return typeof value === 'function';}
9870 * Determines if a value is a regular expression object.
9873 * @param {*} value Reference to check.
9874 * @returns {boolean} True if `value` is a `RegExp`.
9876 function isRegExp(value) {
9877 return toString.call(value) === '[object RegExp]';
9882 * Checks if `obj` is a window object.
9885 * @param {*} obj Object to check
9886 * @returns {boolean} True if `obj` is a window obj.
9888 function isWindow(obj) {
9889 return obj && obj.window === obj;
9893 function isScope(obj) {
9894 return obj && obj.$evalAsync && obj.$watch;
9898 function isFile(obj) {
9899 return toString.call(obj) === '[object File]';
9903 function isFormData(obj) {
9904 return toString.call(obj) === '[object FormData]';
9908 function isBlob(obj) {
9909 return toString.call(obj) === '[object Blob]';
9913 function isBoolean(value) {
9914 return typeof value === 'boolean';
9918 function isPromiseLike(obj) {
9919 return obj && isFunction(obj.then);
9923 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
9924 function isTypedArray(value) {
9925 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
9928 function isArrayBuffer(obj) {
9929 return toString.call(obj) === '[object ArrayBuffer]';
9933 var trim = function(value) {
9934 return isString(value) ? value.trim() : value;
9938 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
9939 // Prereq: s is a string.
9940 var escapeForRegexp = function(s) {
9941 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
9942 replace(/\x08/g, '\\x08');
9948 * @name angular.isElement
9953 * Determines if a reference is a DOM element (or wrapped jQuery element).
9955 * @param {*} value Reference to check.
9956 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
9958 function isElement(node) {
9960 (node.nodeName // we are a direct element
9961 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
9965 * @param str 'key1,key2,...'
9966 * @returns {object} in the form of {key1:true, key2:true, ...}
9968 function makeMap(str) {
9969 var obj = {}, items = str.split(','), i;
9970 for (i = 0; i < items.length; i++) {
9971 obj[items[i]] = true;
9977 function nodeName_(element) {
9978 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
9981 function includes(array, obj) {
9982 return Array.prototype.indexOf.call(array, obj) != -1;
9985 function arrayRemove(array, value) {
9986 var index = array.indexOf(value);
9988 array.splice(index, 1);
9995 * @name angular.copy
10000 * Creates a deep copy of `source`, which should be an object or an array.
10002 * * If no destination is supplied, a copy of the object or array is created.
10003 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
10004 * are deleted and then all elements/properties from the source are copied to it.
10005 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
10006 * * If `source` is identical to 'destination' an exception will be thrown.
10008 * @param {*} source The source that will be used to make a copy.
10009 * Can be any type, including primitives, `null`, and `undefined`.
10010 * @param {(Object|Array)=} destination Destination into which the source is copied. If
10011 * provided, must be of the same type as `source`.
10012 * @returns {*} The copy or updated `destination`, if `destination` was specified.
10015 <example module="copyExample">
10016 <file name="index.html">
10017 <div ng-controller="ExampleController">
10018 <form novalidate class="simple-form">
10019 Name: <input type="text" ng-model="user.name" /><br />
10020 E-mail: <input type="email" ng-model="user.email" /><br />
10021 Gender: <input type="radio" ng-model="user.gender" value="male" />male
10022 <input type="radio" ng-model="user.gender" value="female" />female<br />
10023 <button ng-click="reset()">RESET</button>
10024 <button ng-click="update(user)">SAVE</button>
10026 <pre>form = {{user | json}}</pre>
10027 <pre>master = {{master | json}}</pre>
10031 angular.module('copyExample', [])
10032 .controller('ExampleController', ['$scope', function($scope) {
10035 $scope.update = function(user) {
10036 // Example with 1 argument
10037 $scope.master= angular.copy(user);
10040 $scope.reset = function() {
10041 // Example with 2 arguments
10042 angular.copy($scope.master, $scope.user);
10051 function copy(source, destination) {
10052 var stackSource = [];
10053 var stackDest = [];
10056 if (isTypedArray(destination) || isArrayBuffer(destination)) {
10057 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
10059 if (source === destination) {
10060 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
10063 // Empty the destination object
10064 if (isArray(destination)) {
10065 destination.length = 0;
10067 forEach(destination, function(value, key) {
10068 if (key !== '$$hashKey') {
10069 delete destination[key];
10074 stackSource.push(source);
10075 stackDest.push(destination);
10076 return copyRecurse(source, destination);
10079 return copyElement(source);
10081 function copyRecurse(source, destination) {
10082 var h = destination.$$hashKey;
10084 if (isArray(source)) {
10085 for (var i = 0, ii = source.length; i < ii; i++) {
10086 destination.push(copyElement(source[i]));
10088 } else if (isBlankObject(source)) {
10089 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
10090 for (key in source) {
10091 destination[key] = copyElement(source[key]);
10093 } else if (source && typeof source.hasOwnProperty === 'function') {
10094 // Slow path, which must rely on hasOwnProperty
10095 for (key in source) {
10096 if (source.hasOwnProperty(key)) {
10097 destination[key] = copyElement(source[key]);
10101 // Slowest path --- hasOwnProperty can't be called as a method
10102 for (key in source) {
10103 if (hasOwnProperty.call(source, key)) {
10104 destination[key] = copyElement(source[key]);
10108 setHashKey(destination, h);
10109 return destination;
10112 function copyElement(source) {
10114 if (!isObject(source)) {
10118 // Already copied values
10119 var index = stackSource.indexOf(source);
10120 if (index !== -1) {
10121 return stackDest[index];
10124 if (isWindow(source) || isScope(source)) {
10125 throw ngMinErr('cpws',
10126 "Can't copy! Making copies of Window or Scope instances is not supported.");
10129 var needsRecurse = false;
10130 var destination = copyType(source);
10132 if (destination === undefined) {
10133 destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
10134 needsRecurse = true;
10137 stackSource.push(source);
10138 stackDest.push(destination);
10140 return needsRecurse
10141 ? copyRecurse(source, destination)
10145 function copyType(source) {
10146 switch (toString.call(source)) {
10147 case '[object Int8Array]':
10148 case '[object Int16Array]':
10149 case '[object Int32Array]':
10150 case '[object Float32Array]':
10151 case '[object Float64Array]':
10152 case '[object Uint8Array]':
10153 case '[object Uint8ClampedArray]':
10154 case '[object Uint16Array]':
10155 case '[object Uint32Array]':
10156 return new source.constructor(copyElement(source.buffer));
10158 case '[object ArrayBuffer]':
10160 if (!source.slice) {
10161 var copied = new ArrayBuffer(source.byteLength);
10162 new Uint8Array(copied).set(new Uint8Array(source));
10165 return source.slice(0);
10167 case '[object Boolean]':
10168 case '[object Number]':
10169 case '[object String]':
10170 case '[object Date]':
10171 return new source.constructor(source.valueOf());
10173 case '[object RegExp]':
10174 var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
10175 re.lastIndex = source.lastIndex;
10179 if (isFunction(source.cloneNode)) {
10180 return source.cloneNode(true);
10186 * Creates a shallow copy of an object, an array or a primitive.
10188 * Assumes that there are no proto properties for objects.
10190 function shallowCopy(src, dst) {
10191 if (isArray(src)) {
10194 for (var i = 0, ii = src.length; i < ii; i++) {
10197 } else if (isObject(src)) {
10200 for (var key in src) {
10201 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
10202 dst[key] = src[key];
10213 * @name angular.equals
10218 * Determines if two objects or two values are equivalent. Supports value types, regular
10219 * expressions, arrays and objects.
10221 * Two objects or values are considered equivalent if at least one of the following is true:
10223 * * Both objects or values pass `===` comparison.
10224 * * Both objects or values are of the same type and all of their properties are equal by
10225 * comparing them with `angular.equals`.
10226 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
10227 * * Both values represent the same regular expression (In JavaScript,
10228 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
10229 * representation matches).
10231 * During a property comparison, properties of `function` type and properties with names
10232 * that begin with `$` are ignored.
10234 * Scope and DOMWindow objects are being compared only by identify (`===`).
10236 * @param {*} o1 Object or value to compare.
10237 * @param {*} o2 Object or value to compare.
10238 * @returns {boolean} True if arguments are equal.
10240 function equals(o1, o2) {
10241 if (o1 === o2) return true;
10242 if (o1 === null || o2 === null) return false;
10243 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
10244 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
10245 if (t1 == t2 && t1 == 'object') {
10247 if (!isArray(o2)) return false;
10248 if ((length = o1.length) == o2.length) {
10249 for (key = 0; key < length; key++) {
10250 if (!equals(o1[key], o2[key])) return false;
10254 } else if (isDate(o1)) {
10255 if (!isDate(o2)) return false;
10256 return equals(o1.getTime(), o2.getTime());
10257 } else if (isRegExp(o1)) {
10258 if (!isRegExp(o2)) return false;
10259 return o1.toString() == o2.toString();
10261 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
10262 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
10263 keySet = createMap();
10265 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
10266 if (!equals(o1[key], o2[key])) return false;
10267 keySet[key] = true;
10270 if (!(key in keySet) &&
10271 key.charAt(0) !== '$' &&
10272 isDefined(o2[key]) &&
10273 !isFunction(o2[key])) return false;
10281 var csp = function() {
10282 if (!isDefined(csp.rules)) {
10285 var ngCspElement = (document.querySelector('[ng-csp]') ||
10286 document.querySelector('[data-ng-csp]'));
10288 if (ngCspElement) {
10289 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
10290 ngCspElement.getAttribute('data-ng-csp');
10292 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
10293 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
10297 noUnsafeEval: noUnsafeEval(),
10298 noInlineStyle: false
10305 function noUnsafeEval() {
10307 /* jshint -W031, -W054 */
10309 /* jshint +W031, +W054 */
10323 * @param {string=} ngJq the name of the library available under `window`
10324 * to be used for angular.element
10326 * Use this directive to force the angular.element library. This should be
10327 * used to force either jqLite by leaving ng-jq blank or setting the name of
10328 * the jquery variable under window (eg. jQuery).
10330 * Since angular looks for this directive when it is loaded (doesn't wait for the
10331 * DOMContentLoaded event), it must be placed on an element that comes before the script
10332 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
10336 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
10339 <html ng-app ng-jq>
10345 * This example shows how to use a jQuery based library of a different name.
10346 * The library name must be available at the top most 'window'.
10349 <html ng-app ng-jq="jQueryLib">
10355 var jq = function() {
10356 if (isDefined(jq.name_)) return jq.name_;
10358 var i, ii = ngAttrPrefixes.length, prefix, name;
10359 for (i = 0; i < ii; ++i) {
10360 prefix = ngAttrPrefixes[i];
10361 if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
10362 name = el.getAttribute(prefix + 'jq');
10367 return (jq.name_ = name);
10370 function concat(array1, array2, index) {
10371 return array1.concat(slice.call(array2, index));
10374 function sliceArgs(args, startIndex) {
10375 return slice.call(args, startIndex || 0);
10382 * @name angular.bind
10387 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
10388 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
10389 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
10390 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
10392 * @param {Object} self Context which `fn` should be evaluated in.
10393 * @param {function()} fn Function to be bound.
10394 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
10395 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
10398 function bind(self, fn) {
10399 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
10400 if (isFunction(fn) && !(fn instanceof RegExp)) {
10401 return curryArgs.length
10403 return arguments.length
10404 ? fn.apply(self, concat(curryArgs, arguments, 0))
10405 : fn.apply(self, curryArgs);
10408 return arguments.length
10409 ? fn.apply(self, arguments)
10413 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
10419 function toJsonReplacer(key, value) {
10422 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
10424 } else if (isWindow(value)) {
10426 } else if (value && document === value) {
10428 } else if (isScope(value)) {
10438 * @name angular.toJson
10443 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
10444 * stripped since angular uses this notation internally.
10446 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
10447 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
10448 * If set to an integer, the JSON output will contain that many spaces per indentation.
10449 * @returns {string|undefined} JSON-ified string representing `obj`.
10451 function toJson(obj, pretty) {
10452 if (isUndefined(obj)) return undefined;
10453 if (!isNumber(pretty)) {
10454 pretty = pretty ? 2 : null;
10456 return JSON.stringify(obj, toJsonReplacer, pretty);
10462 * @name angular.fromJson
10467 * Deserializes a JSON string.
10469 * @param {string} json JSON string to deserialize.
10470 * @returns {Object|Array|string|number} Deserialized JSON string.
10472 function fromJson(json) {
10473 return isString(json)
10479 var ALL_COLONS = /:/g;
10480 function timezoneToOffset(timezone, fallback) {
10481 // IE/Edge do not "understand" colon (`:`) in timezone
10482 timezone = timezone.replace(ALL_COLONS, '');
10483 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
10484 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
10488 function addDateMinutes(date, minutes) {
10489 date = new Date(date.getTime());
10490 date.setMinutes(date.getMinutes() + minutes);
10495 function convertTimezoneToLocal(date, timezone, reverse) {
10496 reverse = reverse ? -1 : 1;
10497 var dateTimezoneOffset = date.getTimezoneOffset();
10498 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
10499 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
10504 * @returns {string} Returns the string representation of the element.
10506 function startingTag(element) {
10507 element = jqLite(element).clone();
10509 // turns out IE does not let you set .html() on elements which
10510 // are not allowed to have children. So we just ignore it.
10513 var elemHtml = jqLite('<div>').append(element).html();
10515 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
10517 match(/^(<[^>]+>)/)[1].
10518 replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
10520 return lowercase(elemHtml);
10526 /////////////////////////////////////////////////
10529 * Tries to decode the URI component without throwing an exception.
10532 * @param str value potential URI component to check.
10533 * @returns {boolean} True if `value` can be decoded
10534 * with the decodeURIComponent function.
10536 function tryDecodeURIComponent(value) {
10538 return decodeURIComponent(value);
10540 // Ignore any invalid uri component
10546 * Parses an escaped url query string into key-value pairs.
10547 * @returns {Object.<string,boolean|Array>}
10549 function parseKeyValue(/**string*/keyValue) {
10551 forEach((keyValue || "").split('&'), function(keyValue) {
10552 var splitPoint, key, val;
10554 key = keyValue = keyValue.replace(/\+/g,'%20');
10555 splitPoint = keyValue.indexOf('=');
10556 if (splitPoint !== -1) {
10557 key = keyValue.substring(0, splitPoint);
10558 val = keyValue.substring(splitPoint + 1);
10560 key = tryDecodeURIComponent(key);
10561 if (isDefined(key)) {
10562 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
10563 if (!hasOwnProperty.call(obj, key)) {
10565 } else if (isArray(obj[key])) {
10566 obj[key].push(val);
10568 obj[key] = [obj[key],val];
10576 function toKeyValue(obj) {
10578 forEach(obj, function(value, key) {
10579 if (isArray(value)) {
10580 forEach(value, function(arrayValue) {
10581 parts.push(encodeUriQuery(key, true) +
10582 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
10585 parts.push(encodeUriQuery(key, true) +
10586 (value === true ? '' : '=' + encodeUriQuery(value, true)));
10589 return parts.length ? parts.join('&') : '';
10594 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
10595 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
10598 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
10599 * pct-encoded = "%" HEXDIG HEXDIG
10600 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
10601 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
10602 * / "*" / "+" / "," / ";" / "="
10604 function encodeUriSegment(val) {
10605 return encodeUriQuery(val, true).
10606 replace(/%26/gi, '&').
10607 replace(/%3D/gi, '=').
10608 replace(/%2B/gi, '+');
10613 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
10614 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
10615 * encoded per http://tools.ietf.org/html/rfc3986:
10616 * query = *( pchar / "/" / "?" )
10617 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
10618 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
10619 * pct-encoded = "%" HEXDIG HEXDIG
10620 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
10621 * / "*" / "+" / "," / ";" / "="
10623 function encodeUriQuery(val, pctEncodeSpaces) {
10624 return encodeURIComponent(val).
10625 replace(/%40/gi, '@').
10626 replace(/%3A/gi, ':').
10627 replace(/%24/g, '$').
10628 replace(/%2C/gi, ',').
10629 replace(/%3B/gi, ';').
10630 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
10633 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
10635 function getNgAttribute(element, ngAttr) {
10636 var attr, i, ii = ngAttrPrefixes.length;
10637 for (i = 0; i < ii; ++i) {
10638 attr = ngAttrPrefixes[i] + ngAttr;
10639 if (isString(attr = element.getAttribute(attr))) {
10652 * @param {angular.Module} ngApp an optional application
10653 * {@link angular.module module} name to load.
10654 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
10655 * created in "strict-di" mode. This means that the application will fail to invoke functions which
10656 * do not use explicit function annotation (and are thus unsuitable for minification), as described
10657 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
10658 * tracking down the root of these bugs.
10662 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
10663 * designates the **root element** of the application and is typically placed near the root element
10664 * of the page - e.g. on the `<body>` or `<html>` tags.
10666 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
10667 * found in the document will be used to define the root element to auto-bootstrap as an
10668 * application. To run multiple applications in an HTML document you must manually bootstrap them using
10669 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
10671 * You can specify an **AngularJS module** to be used as the root module for the application. This
10672 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
10673 * should contain the application code needed or have dependencies on other modules that will
10674 * contain the code. See {@link angular.module} for more information.
10676 * In the example below if the `ngApp` directive were not placed on the `html` element then the
10677 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
10678 * would not be resolved to `3`.
10680 * `ngApp` is the easiest, and most common way to bootstrap an application.
10682 <example module="ngAppDemo">
10683 <file name="index.html">
10684 <div ng-controller="ngAppDemoController">
10685 I can add: {{a}} + {{b}} = {{ a+b }}
10688 <file name="script.js">
10689 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
10696 * Using `ngStrictDi`, you would see something like this:
10698 <example ng-app-included="true">
10699 <file name="index.html">
10700 <div ng-app="ngAppStrictDemo" ng-strict-di>
10701 <div ng-controller="GoodController1">
10702 I can add: {{a}} + {{b}} = {{ a+b }}
10704 <p>This renders because the controller does not fail to
10705 instantiate, by using explicit annotation style (see
10706 script.js for details)
10710 <div ng-controller="GoodController2">
10711 Name: <input ng-model="name"><br />
10714 <p>This renders because the controller does not fail to
10715 instantiate, by using explicit annotation style
10716 (see script.js for details)
10720 <div ng-controller="BadController">
10721 I can add: {{a}} + {{b}} = {{ a+b }}
10723 <p>The controller could not be instantiated, due to relying
10724 on automatic function annotations (which are disabled in
10725 strict mode). As such, the content of this section is not
10726 interpolated, and there should be an error in your web console.
10731 <file name="script.js">
10732 angular.module('ngAppStrictDemo', [])
10733 // BadController will fail to instantiate, due to relying on automatic function annotation,
10734 // rather than an explicit annotation
10735 .controller('BadController', function($scope) {
10739 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
10740 // due to using explicit annotations using the array style and $inject property, respectively.
10741 .controller('GoodController1', ['$scope', function($scope) {
10745 .controller('GoodController2', GoodController2);
10746 function GoodController2($scope) {
10747 $scope.name = "World";
10749 GoodController2.$inject = ['$scope'];
10751 <file name="style.css">
10752 div[ng-controller] {
10753 margin-bottom: 1em;
10754 -webkit-border-radius: 4px;
10755 border-radius: 4px;
10759 div[ng-controller^=Good] {
10760 border-color: #d6e9c6;
10761 background-color: #dff0d8;
10764 div[ng-controller^=Bad] {
10765 border-color: #ebccd1;
10766 background-color: #f2dede;
10773 function angularInit(element, bootstrap) {
10778 // The element `element` has priority over any other element
10779 forEach(ngAttrPrefixes, function(prefix) {
10780 var name = prefix + 'app';
10782 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
10783 appElement = element;
10784 module = element.getAttribute(name);
10787 forEach(ngAttrPrefixes, function(prefix) {
10788 var name = prefix + 'app';
10791 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
10792 appElement = candidate;
10793 module = candidate.getAttribute(name);
10797 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
10798 bootstrap(appElement, module ? [module] : [], config);
10804 * @name angular.bootstrap
10807 * Use this function to manually start up angular application.
10809 * See: {@link guide/bootstrap Bootstrap}
10811 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
10812 * They must use {@link ng.directive:ngApp ngApp}.
10814 * Angular will detect if it has been loaded into the browser more than once and only allow the
10815 * first loaded script to be bootstrapped and will report a warning to the browser console for
10816 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
10817 * multiple instances of Angular try to work on the DOM.
10823 * <div ng-controller="WelcomeController">
10827 * <script src="angular.js"></script>
10829 * var app = angular.module('demo', [])
10830 * .controller('WelcomeController', function($scope) {
10831 * $scope.greeting = 'Welcome!';
10833 * angular.bootstrap(document, ['demo']);
10839 * @param {DOMElement} element DOM element which is the root of angular application.
10840 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
10841 * Each item in the array should be the name of a predefined module or a (DI annotated)
10842 * function that will be invoked by the injector as a `config` block.
10843 * See: {@link angular.module modules}
10844 * @param {Object=} config an object for defining configuration options for the application. The
10845 * following keys are supported:
10847 * * `strictDi` - disable automatic function annotation for the application. This is meant to
10848 * assist in finding bugs which break minified code. Defaults to `false`.
10850 * @returns {auto.$injector} Returns the newly created injector for this app.
10852 function bootstrap(element, modules, config) {
10853 if (!isObject(config)) config = {};
10854 var defaultConfig = {
10857 config = extend(defaultConfig, config);
10858 var doBootstrap = function() {
10859 element = jqLite(element);
10861 if (element.injector()) {
10862 var tag = (element[0] === document) ? 'document' : startingTag(element);
10863 //Encode angle brackets to prevent input from being sanitized to empty string #8683
10866 "App Already Bootstrapped with this Element '{0}'",
10867 tag.replace(/</,'<').replace(/>/,'>'));
10870 modules = modules || [];
10871 modules.unshift(['$provide', function($provide) {
10872 $provide.value('$rootElement', element);
10875 if (config.debugInfoEnabled) {
10876 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
10877 modules.push(['$compileProvider', function($compileProvider) {
10878 $compileProvider.debugInfoEnabled(true);
10882 modules.unshift('ng');
10883 var injector = createInjector(modules, config.strictDi);
10884 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
10885 function bootstrapApply(scope, element, compile, injector) {
10886 scope.$apply(function() {
10887 element.data('$injector', injector);
10888 compile(element)(scope);
10895 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
10896 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
10898 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
10899 config.debugInfoEnabled = true;
10900 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
10903 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
10904 return doBootstrap();
10907 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
10908 angular.resumeBootstrap = function(extraModules) {
10909 forEach(extraModules, function(module) {
10910 modules.push(module);
10912 return doBootstrap();
10915 if (isFunction(angular.resumeDeferredBootstrap)) {
10916 angular.resumeDeferredBootstrap();
10922 * @name angular.reloadWithDebugInfo
10925 * Use this function to reload the current application with debug information turned on.
10926 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
10928 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
10930 function reloadWithDebugInfo() {
10931 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
10932 window.location.reload();
10936 * @name angular.getTestability
10939 * Get the testability service for the instance of Angular on the given
10941 * @param {DOMElement} element DOM element which is the root of angular application.
10943 function getTestability(rootElement) {
10944 var injector = angular.element(rootElement).injector();
10946 throw ngMinErr('test',
10947 'no injector found for element argument to getTestability');
10949 return injector.get('$$testability');
10952 var SNAKE_CASE_REGEXP = /[A-Z]/g;
10953 function snake_case(name, separator) {
10954 separator = separator || '_';
10955 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
10956 return (pos ? separator : '') + letter.toLowerCase();
10960 var bindJQueryFired = false;
10961 function bindJQuery() {
10962 var originalCleanData;
10964 if (bindJQueryFired) {
10968 // bind to jQuery if present;
10970 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
10971 !jqName ? undefined : // use jqLite
10972 window[jqName]; // use jQuery specified by `ngJq`
10974 // Use jQuery if it exists with proper functionality, otherwise default to us.
10975 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
10976 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
10977 // versions. It will not work for sure with jQuery <1.7, though.
10978 if (jQuery && jQuery.fn.on) {
10980 extend(jQuery.fn, {
10981 scope: JQLitePrototype.scope,
10982 isolateScope: JQLitePrototype.isolateScope,
10983 controller: JQLitePrototype.controller,
10984 injector: JQLitePrototype.injector,
10985 inheritedData: JQLitePrototype.inheritedData
10988 // All nodes removed from the DOM via various jQuery APIs like .remove()
10989 // are passed through jQuery.cleanData. Monkey-patch this method to fire
10990 // the $destroy event on all removed nodes.
10991 originalCleanData = jQuery.cleanData;
10992 jQuery.cleanData = function(elems) {
10994 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
10995 events = jQuery._data(elem, "events");
10996 if (events && events.$destroy) {
10997 jQuery(elem).triggerHandler('$destroy');
11000 originalCleanData(elems);
11006 angular.element = jqLite;
11008 // Prevent double-proxying.
11009 bindJQueryFired = true;
11013 * throw error if the argument is falsy.
11015 function assertArg(arg, name, reason) {
11017 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
11022 function assertArgFn(arg, name, acceptArrayAnnotation) {
11023 if (acceptArrayAnnotation && isArray(arg)) {
11024 arg = arg[arg.length - 1];
11027 assertArg(isFunction(arg), name, 'not a function, got ' +
11028 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
11033 * throw error if the name given is hasOwnProperty
11034 * @param {String} name the name to test
11035 * @param {String} context the context in which the name is used, such as module or directive
11037 function assertNotHasOwnProperty(name, context) {
11038 if (name === 'hasOwnProperty') {
11039 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
11044 * Return the value accessible from the object by path. Any undefined traversals are ignored
11045 * @param {Object} obj starting object
11046 * @param {String} path path to traverse
11047 * @param {boolean} [bindFnToScope=true]
11048 * @returns {Object} value as accessible by path
11050 //TODO(misko): this function needs to be removed
11051 function getter(obj, path, bindFnToScope) {
11052 if (!path) return obj;
11053 var keys = path.split('.');
11055 var lastInstance = obj;
11056 var len = keys.length;
11058 for (var i = 0; i < len; i++) {
11061 obj = (lastInstance = obj)[key];
11064 if (!bindFnToScope && isFunction(obj)) {
11065 return bind(lastInstance, obj);
11071 * Return the DOM siblings between the first and last node in the given array.
11072 * @param {Array} array like object
11073 * @returns {Array} the inputted object or a jqLite collection containing the nodes
11075 function getBlockNodes(nodes) {
11076 // TODO(perf): update `nodes` instead of creating a new object?
11077 var node = nodes[0];
11078 var endNode = nodes[nodes.length - 1];
11081 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
11082 if (blockNodes || nodes[i] !== node) {
11084 blockNodes = jqLite(slice.call(nodes, 0, i));
11086 blockNodes.push(node);
11090 return blockNodes || nodes;
11095 * Creates a new object without a prototype. This object is useful for lookup without having to
11096 * guard against prototypically inherited properties via hasOwnProperty.
11098 * Related micro-benchmarks:
11099 * - http://jsperf.com/object-create2
11100 * - http://jsperf.com/proto-map-lookup/2
11101 * - http://jsperf.com/for-in-vs-object-keys2
11103 * @returns {Object}
11105 function createMap() {
11106 return Object.create(null);
11109 var NODE_TYPE_ELEMENT = 1;
11110 var NODE_TYPE_ATTRIBUTE = 2;
11111 var NODE_TYPE_TEXT = 3;
11112 var NODE_TYPE_COMMENT = 8;
11113 var NODE_TYPE_DOCUMENT = 9;
11114 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
11118 * @name angular.Module
11122 * Interface for configuring angular {@link angular.module modules}.
11125 function setupModuleLoader(window) {
11127 var $injectorMinErr = minErr('$injector');
11128 var ngMinErr = minErr('ng');
11130 function ensure(obj, name, factory) {
11131 return obj[name] || (obj[name] = factory());
11134 var angular = ensure(window, 'angular', Object);
11136 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
11137 angular.$$minErr = angular.$$minErr || minErr;
11139 return ensure(angular, 'module', function() {
11140 /** @type {Object.<string, angular.Module>} */
11145 * @name angular.module
11149 * The `angular.module` is a global place for creating, registering and retrieving Angular
11151 * All modules (angular core or 3rd party) that should be available to an application must be
11152 * registered using this mechanism.
11154 * Passing one argument retrieves an existing {@link angular.Module},
11155 * whereas passing more than one argument creates a new {@link angular.Module}
11160 * A module is a collection of services, directives, controllers, filters, and configuration information.
11161 * `angular.module` is used to configure the {@link auto.$injector $injector}.
11164 * // Create a new module
11165 * var myModule = angular.module('myModule', []);
11167 * // register a new service
11168 * myModule.value('appName', 'MyCoolApp');
11170 * // configure existing services inside initialization blocks.
11171 * myModule.config(['$locationProvider', function($locationProvider) {
11172 * // Configure existing providers
11173 * $locationProvider.hashPrefix('!');
11177 * Then you can create an injector and load your modules like this:
11180 * var injector = angular.injector(['ng', 'myModule'])
11183 * However it's more likely that you'll just use
11184 * {@link ng.directive:ngApp ngApp} or
11185 * {@link angular.bootstrap} to simplify this process for you.
11187 * @param {!string} name The name of the module to create or retrieve.
11188 * @param {!Array.<string>=} requires If specified then new module is being created. If
11189 * unspecified then the module is being retrieved for further configuration.
11190 * @param {Function=} configFn Optional configuration function for the module. Same as
11191 * {@link angular.Module#config Module#config()}.
11192 * @returns {angular.Module} new module with the {@link angular.Module} api.
11194 return function module(name, requires, configFn) {
11195 var assertNotHasOwnProperty = function(name, context) {
11196 if (name === 'hasOwnProperty') {
11197 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
11201 assertNotHasOwnProperty(name, 'module');
11202 if (requires && modules.hasOwnProperty(name)) {
11203 modules[name] = null;
11205 return ensure(modules, name, function() {
11207 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
11208 "the module name or forgot to load it. If registering a module ensure that you " +
11209 "specify the dependencies as the second argument.", name);
11212 /** @type {!Array.<Array.<*>>} */
11213 var invokeQueue = [];
11215 /** @type {!Array.<Function>} */
11216 var configBlocks = [];
11218 /** @type {!Array.<Function>} */
11219 var runBlocks = [];
11221 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
11223 /** @type {angular.Module} */
11224 var moduleInstance = {
11226 _invokeQueue: invokeQueue,
11227 _configBlocks: configBlocks,
11228 _runBlocks: runBlocks,
11232 * @name angular.Module#requires
11236 * Holds the list of modules which the injector will load before the current module is
11239 requires: requires,
11243 * @name angular.Module#name
11247 * Name of the module.
11254 * @name angular.Module#provider
11256 * @param {string} name service name
11257 * @param {Function} providerType Construction function for creating new instance of the
11260 * See {@link auto.$provide#provider $provide.provider()}.
11262 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
11266 * @name angular.Module#factory
11268 * @param {string} name service name
11269 * @param {Function} providerFunction Function for creating new instance of the service.
11271 * See {@link auto.$provide#factory $provide.factory()}.
11273 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
11277 * @name angular.Module#service
11279 * @param {string} name service name
11280 * @param {Function} constructor A constructor function that will be instantiated.
11282 * See {@link auto.$provide#service $provide.service()}.
11284 service: invokeLaterAndSetModuleName('$provide', 'service'),
11288 * @name angular.Module#value
11290 * @param {string} name service name
11291 * @param {*} object Service instance object.
11293 * See {@link auto.$provide#value $provide.value()}.
11295 value: invokeLater('$provide', 'value'),
11299 * @name angular.Module#constant
11301 * @param {string} name constant name
11302 * @param {*} object Constant value.
11304 * Because the constants are fixed, they get applied before other provide methods.
11305 * See {@link auto.$provide#constant $provide.constant()}.
11307 constant: invokeLater('$provide', 'constant', 'unshift'),
11311 * @name angular.Module#decorator
11313 * @param {string} The name of the service to decorate.
11314 * @param {Function} This function will be invoked when the service needs to be
11315 * instantiated and should return the decorated service instance.
11317 * See {@link auto.$provide#decorator $provide.decorator()}.
11319 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
11323 * @name angular.Module#animation
11325 * @param {string} name animation name
11326 * @param {Function} animationFactory Factory function for creating new instance of an
11330 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
11333 * Defines an animation hook that can be later used with
11334 * {@link $animate $animate} service and directives that use this service.
11337 * module.animation('.animation-name', function($inject1, $inject2) {
11339 * eventName : function(element, done) {
11340 * //code to run the animation
11341 * //once complete, then run done()
11342 * return function cancellationFunction(element) {
11343 * //code to cancel the animation
11350 * See {@link ng.$animateProvider#register $animateProvider.register()} and
11351 * {@link ngAnimate ngAnimate module} for more information.
11353 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
11357 * @name angular.Module#filter
11359 * @param {string} name Filter name - this must be a valid angular expression identifier
11360 * @param {Function} filterFactory Factory function for creating new instance of filter.
11362 * See {@link ng.$filterProvider#register $filterProvider.register()}.
11364 * <div class="alert alert-warning">
11365 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
11366 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
11367 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
11368 * (`myapp_subsection_filterx`).
11371 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
11375 * @name angular.Module#controller
11377 * @param {string|Object} name Controller name, or an object map of controllers where the
11378 * keys are the names and the values are the constructors.
11379 * @param {Function} constructor Controller constructor function.
11381 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
11383 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
11387 * @name angular.Module#directive
11389 * @param {string|Object} name Directive name, or an object map of directives where the
11390 * keys are the names and the values are the factories.
11391 * @param {Function} directiveFactory Factory function for creating new instance of
11394 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
11396 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
11400 * @name angular.Module#component
11402 * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
11403 * @param {Object} options Component definition object (a simplified
11404 * {@link ng.$compile#directive-definition-object directive definition object})
11407 * See {@link ng.$compileProvider#component $compileProvider.component()}.
11409 component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
11413 * @name angular.Module#config
11415 * @param {Function} configFn Execute this function on module load. Useful for service
11418 * Use this method to register work which needs to be performed on module loading.
11419 * For more about how to configure services, see
11420 * {@link providers#provider-recipe Provider Recipe}.
11426 * @name angular.Module#run
11428 * @param {Function} initializationFn Execute this function after injector creation.
11429 * Useful for application initialization.
11431 * Use this method to register work which should be performed when the injector is done
11432 * loading all modules.
11434 run: function(block) {
11435 runBlocks.push(block);
11444 return moduleInstance;
11447 * @param {string} provider
11448 * @param {string} method
11449 * @param {String=} insertMethod
11450 * @returns {angular.Module}
11452 function invokeLater(provider, method, insertMethod, queue) {
11453 if (!queue) queue = invokeQueue;
11454 return function() {
11455 queue[insertMethod || 'push']([provider, method, arguments]);
11456 return moduleInstance;
11461 * @param {string} provider
11462 * @param {string} method
11463 * @returns {angular.Module}
11465 function invokeLaterAndSetModuleName(provider, method) {
11466 return function(recipeName, factoryFunction) {
11467 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
11468 invokeQueue.push([provider, method, arguments]);
11469 return moduleInstance;
11478 /* global: toDebugString: true */
11480 function serializeObject(obj) {
11483 return JSON.stringify(obj, function(key, val) {
11484 val = toJsonReplacer(key, val);
11485 if (isObject(val)) {
11487 if (seen.indexOf(val) >= 0) return '...';
11495 function toDebugString(obj) {
11496 if (typeof obj === 'function') {
11497 return obj.toString().replace(/ \{[\s\S]*$/, '');
11498 } else if (isUndefined(obj)) {
11499 return 'undefined';
11500 } else if (typeof obj !== 'string') {
11501 return serializeObject(obj);
11506 /* global angularModule: true,
11511 htmlAnchorDirective,
11520 ngBindHtmlDirective,
11521 ngBindTemplateDirective,
11523 ngClassEvenDirective,
11524 ngClassOddDirective,
11526 ngControllerDirective,
11530 ngIncludeDirective,
11531 ngIncludeFillContentDirective,
11533 ngNonBindableDirective,
11534 ngPluralizeDirective,
11539 ngSwitchWhenDirective,
11540 ngSwitchDefaultDirective,
11541 ngOptionsDirective,
11542 ngTranscludeDirective,
11550 minlengthDirective,
11551 minlengthDirective,
11552 maxlengthDirective,
11553 maxlengthDirective,
11555 ngModelOptionsDirective,
11556 ngAttributeAliasDirectives,
11559 $AnchorScrollProvider,
11561 $CoreAnimateCssProvider,
11562 $$CoreAnimateJsProvider,
11563 $$CoreAnimateQueueProvider,
11564 $$AnimateRunnerFactoryProvider,
11565 $$AnimateAsyncRunFactoryProvider,
11567 $CacheFactoryProvider,
11568 $ControllerProvider,
11571 $ExceptionHandlerProvider,
11573 $$ForceReflowProvider,
11574 $InterpolateProvider,
11578 $HttpParamSerializerProvider,
11579 $HttpParamSerializerJQLikeProvider,
11580 $HttpBackendProvider,
11581 $xhrFactoryProvider,
11585 $RootScopeProvider,
11588 $$SanitizeUriProvider,
11590 $SceDelegateProvider,
11592 $TemplateCacheProvider,
11593 $TemplateRequestProvider,
11594 $$TestabilityProvider,
11599 $$CookieReaderProvider
11605 * @name angular.version
11608 * An object that contains information about the current AngularJS version.
11610 * This object has the following properties:
11612 * - `full` – `{string}` – Full version string, such as "0.9.18".
11613 * - `major` – `{number}` – Major version number, such as "0".
11614 * - `minor` – `{number}` – Minor version number, such as "9".
11615 * - `dot` – `{number}` – Dot version number, such as "18".
11616 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
11619 full: '1.5.0', // all of these placeholder strings will be replaced by grunt's
11620 major: 1, // package task
11623 codeName: 'ennoblement-facilitation'
11627 function publishExternalAPI(angular) {
11629 'bootstrap': bootstrap,
11635 'forEach': forEach,
11636 'injector': createInjector,
11640 'fromJson': fromJson,
11641 'identity': identity,
11642 'isUndefined': isUndefined,
11643 'isDefined': isDefined,
11644 'isString': isString,
11645 'isFunction': isFunction,
11646 'isObject': isObject,
11647 'isNumber': isNumber,
11648 'isElement': isElement,
11649 'isArray': isArray,
11650 'version': version,
11652 'lowercase': lowercase,
11653 'uppercase': uppercase,
11654 'callbacks': {counter: 0},
11655 'getTestability': getTestability,
11656 '$$minErr': minErr,
11658 'reloadWithDebugInfo': reloadWithDebugInfo
11661 angularModule = setupModuleLoader(window);
11663 angularModule('ng', ['ngLocale'], ['$provide',
11664 function ngModule($provide) {
11665 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
11666 $provide.provider({
11667 $$sanitizeUri: $$SanitizeUriProvider
11669 $provide.provider('$compile', $CompileProvider).
11671 a: htmlAnchorDirective,
11672 input: inputDirective,
11673 textarea: inputDirective,
11674 form: formDirective,
11675 script: scriptDirective,
11676 select: selectDirective,
11677 style: styleDirective,
11678 option: optionDirective,
11679 ngBind: ngBindDirective,
11680 ngBindHtml: ngBindHtmlDirective,
11681 ngBindTemplate: ngBindTemplateDirective,
11682 ngClass: ngClassDirective,
11683 ngClassEven: ngClassEvenDirective,
11684 ngClassOdd: ngClassOddDirective,
11685 ngCloak: ngCloakDirective,
11686 ngController: ngControllerDirective,
11687 ngForm: ngFormDirective,
11688 ngHide: ngHideDirective,
11689 ngIf: ngIfDirective,
11690 ngInclude: ngIncludeDirective,
11691 ngInit: ngInitDirective,
11692 ngNonBindable: ngNonBindableDirective,
11693 ngPluralize: ngPluralizeDirective,
11694 ngRepeat: ngRepeatDirective,
11695 ngShow: ngShowDirective,
11696 ngStyle: ngStyleDirective,
11697 ngSwitch: ngSwitchDirective,
11698 ngSwitchWhen: ngSwitchWhenDirective,
11699 ngSwitchDefault: ngSwitchDefaultDirective,
11700 ngOptions: ngOptionsDirective,
11701 ngTransclude: ngTranscludeDirective,
11702 ngModel: ngModelDirective,
11703 ngList: ngListDirective,
11704 ngChange: ngChangeDirective,
11705 pattern: patternDirective,
11706 ngPattern: patternDirective,
11707 required: requiredDirective,
11708 ngRequired: requiredDirective,
11709 minlength: minlengthDirective,
11710 ngMinlength: minlengthDirective,
11711 maxlength: maxlengthDirective,
11712 ngMaxlength: maxlengthDirective,
11713 ngValue: ngValueDirective,
11714 ngModelOptions: ngModelOptionsDirective
11717 ngInclude: ngIncludeFillContentDirective
11719 directive(ngAttributeAliasDirectives).
11720 directive(ngEventDirectives);
11721 $provide.provider({
11722 $anchorScroll: $AnchorScrollProvider,
11723 $animate: $AnimateProvider,
11724 $animateCss: $CoreAnimateCssProvider,
11725 $$animateJs: $$CoreAnimateJsProvider,
11726 $$animateQueue: $$CoreAnimateQueueProvider,
11727 $$AnimateRunner: $$AnimateRunnerFactoryProvider,
11728 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
11729 $browser: $BrowserProvider,
11730 $cacheFactory: $CacheFactoryProvider,
11731 $controller: $ControllerProvider,
11732 $document: $DocumentProvider,
11733 $exceptionHandler: $ExceptionHandlerProvider,
11734 $filter: $FilterProvider,
11735 $$forceReflow: $$ForceReflowProvider,
11736 $interpolate: $InterpolateProvider,
11737 $interval: $IntervalProvider,
11738 $http: $HttpProvider,
11739 $httpParamSerializer: $HttpParamSerializerProvider,
11740 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
11741 $httpBackend: $HttpBackendProvider,
11742 $xhrFactory: $xhrFactoryProvider,
11743 $location: $LocationProvider,
11744 $log: $LogProvider,
11745 $parse: $ParseProvider,
11746 $rootScope: $RootScopeProvider,
11749 $sce: $SceProvider,
11750 $sceDelegate: $SceDelegateProvider,
11751 $sniffer: $SnifferProvider,
11752 $templateCache: $TemplateCacheProvider,
11753 $templateRequest: $TemplateRequestProvider,
11754 $$testability: $$TestabilityProvider,
11755 $timeout: $TimeoutProvider,
11756 $window: $WindowProvider,
11757 $$rAF: $$RAFProvider,
11758 $$jqLite: $$jqLiteProvider,
11759 $$HashMap: $$HashMapProvider,
11760 $$cookieReader: $$CookieReaderProvider
11766 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
11767 * Any commits to this file should be reviewed with security in mind. *
11768 * Changes to this file can potentially create security vulnerabilities. *
11769 * An approval from 2 Core members with history of modifying *
11770 * this file is required. *
11772 * Does the change somehow allow for arbitrary javascript to be executed? *
11773 * Or allows for someone to change the prototype of built-in objects? *
11774 * Or gives undesired access to variables likes document or window? *
11775 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
11777 /* global JQLitePrototype: true,
11778 addEventListenerFn: true,
11779 removeEventListenerFn: true,
11780 BOOLEAN_ATTR: true,
11781 ALIASED_ATTR: true,
11784 //////////////////////////////////
11786 //////////////////////////////////
11790 * @name angular.element
11795 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
11797 * If jQuery is available, `angular.element` is an alias for the
11798 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
11799 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
11801 * jqLite is a tiny, API-compatible subset of jQuery that allows
11802 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
11803 * commonly needed functionality with the goal of having a very small footprint.
11805 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
11806 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
11807 * specific version of jQuery if multiple versions exist on the page.
11809 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
11810 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
11812 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
11813 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
11814 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
11816 * ## Angular's jqLite
11817 * jqLite provides only the following jQuery methods:
11819 * - [`addClass()`](http://api.jquery.com/addClass/)
11820 * - [`after()`](http://api.jquery.com/after/)
11821 * - [`append()`](http://api.jquery.com/append/)
11822 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
11823 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
11824 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
11825 * - [`clone()`](http://api.jquery.com/clone/)
11826 * - [`contents()`](http://api.jquery.com/contents/)
11827 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
11828 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
11829 * - [`data()`](http://api.jquery.com/data/)
11830 * - [`detach()`](http://api.jquery.com/detach/)
11831 * - [`empty()`](http://api.jquery.com/empty/)
11832 * - [`eq()`](http://api.jquery.com/eq/)
11833 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
11834 * - [`hasClass()`](http://api.jquery.com/hasClass/)
11835 * - [`html()`](http://api.jquery.com/html/)
11836 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
11837 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
11838 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
11839 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
11840 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
11841 * - [`prepend()`](http://api.jquery.com/prepend/)
11842 * - [`prop()`](http://api.jquery.com/prop/)
11843 * - [`ready()`](http://api.jquery.com/ready/)
11844 * - [`remove()`](http://api.jquery.com/remove/)
11845 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
11846 * - [`removeClass()`](http://api.jquery.com/removeClass/)
11847 * - [`removeData()`](http://api.jquery.com/removeData/)
11848 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
11849 * - [`text()`](http://api.jquery.com/text/)
11850 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
11851 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
11852 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
11853 * - [`val()`](http://api.jquery.com/val/)
11854 * - [`wrap()`](http://api.jquery.com/wrap/)
11856 * ## jQuery/jqLite Extras
11857 * Angular also provides the following additional methods and events to both jQuery and jqLite:
11860 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
11861 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
11862 * element before it is removed.
11865 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
11866 * retrieves controller associated with the `ngController` directive. If `name` is provided as
11867 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
11869 * - `injector()` - retrieves the injector of the current element or its parent.
11870 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
11871 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
11873 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
11874 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
11875 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
11876 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
11877 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
11878 * parent element is reached.
11880 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
11881 * @returns {Object} jQuery object.
11884 JQLite.expando = 'ng339';
11886 var jqCache = JQLite.cache = {},
11888 addEventListenerFn = function(element, type, fn) {
11889 element.addEventListener(type, fn, false);
11891 removeEventListenerFn = function(element, type, fn) {
11892 element.removeEventListener(type, fn, false);
11896 * !!! This is an undocumented "private" function !!!
11898 JQLite._data = function(node) {
11899 //jQuery always returns an object on cache miss
11900 return this.cache[node[this.expando]] || {};
11903 function jqNextId() { return ++jqId; }
11906 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
11907 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
11908 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
11909 var jqLiteMinErr = minErr('jqLite');
11912 * Converts snake_case to camelCase.
11913 * Also there is special case for Moz prefix starting with upper case letter.
11914 * @param name Name to normalize
11916 function camelCase(name) {
11918 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
11919 return offset ? letter.toUpperCase() : letter;
11921 replace(MOZ_HACK_REGEXP, 'Moz$1');
11924 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
11925 var HTML_REGEXP = /<|&#?\w+;/;
11926 var TAG_NAME_REGEXP = /<([\w:-]+)/;
11927 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
11930 'option': [1, '<select multiple="multiple">', '</select>'],
11932 'thead': [1, '<table>', '</table>'],
11933 'col': [2, '<table><colgroup>', '</colgroup></table>'],
11934 'tr': [2, '<table><tbody>', '</tbody></table>'],
11935 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
11936 '_default': [0, "", ""]
11939 wrapMap.optgroup = wrapMap.option;
11940 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
11941 wrapMap.th = wrapMap.td;
11944 function jqLiteIsTextNode(html) {
11945 return !HTML_REGEXP.test(html);
11948 function jqLiteAcceptsData(node) {
11949 // The window object can accept data but has no nodeType
11950 // Otherwise we are only interested in elements (1) and documents (9)
11951 var nodeType = node.nodeType;
11952 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
11955 function jqLiteHasData(node) {
11956 for (var key in jqCache[node.ng339]) {
11962 function jqLiteCleanData(nodes) {
11963 for (var i = 0, ii = nodes.length; i < ii; i++) {
11964 jqLiteRemoveData(nodes[i]);
11968 function jqLiteBuildFragment(html, context) {
11969 var tmp, tag, wrap,
11970 fragment = context.createDocumentFragment(),
11973 if (jqLiteIsTextNode(html)) {
11974 // Convert non-html into a text node
11975 nodes.push(context.createTextNode(html));
11977 // Convert html into DOM nodes
11978 tmp = tmp || fragment.appendChild(context.createElement("div"));
11979 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
11980 wrap = wrapMap[tag] || wrapMap._default;
11981 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
11983 // Descend through wrappers to the right content
11986 tmp = tmp.lastChild;
11989 nodes = concat(nodes, tmp.childNodes);
11991 tmp = fragment.firstChild;
11992 tmp.textContent = "";
11995 // Remove wrapper from fragment
11996 fragment.textContent = "";
11997 fragment.innerHTML = ""; // Clear inner HTML
11998 forEach(nodes, function(node) {
11999 fragment.appendChild(node);
12005 function jqLiteParseHTML(html, context) {
12006 context = context || document;
12009 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
12010 return [context.createElement(parsed[1])];
12013 if ((parsed = jqLiteBuildFragment(html, context))) {
12014 return parsed.childNodes;
12020 function jqLiteWrapNode(node, wrapper) {
12021 var parent = node.parentNode;
12024 parent.replaceChild(wrapper, node);
12027 wrapper.appendChild(node);
12031 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
12032 var jqLiteContains = Node.prototype.contains || function(arg) {
12033 // jshint bitwise: false
12034 return !!(this.compareDocumentPosition(arg) & 16);
12035 // jshint bitwise: true
12038 /////////////////////////////////////////////
12039 function JQLite(element) {
12040 if (element instanceof JQLite) {
12046 if (isString(element)) {
12047 element = trim(element);
12048 argIsString = true;
12050 if (!(this instanceof JQLite)) {
12051 if (argIsString && element.charAt(0) != '<') {
12052 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
12054 return new JQLite(element);
12058 jqLiteAddNodes(this, jqLiteParseHTML(element));
12060 jqLiteAddNodes(this, element);
12064 function jqLiteClone(element) {
12065 return element.cloneNode(true);
12068 function jqLiteDealoc(element, onlyDescendants) {
12069 if (!onlyDescendants) jqLiteRemoveData(element);
12071 if (element.querySelectorAll) {
12072 var descendants = element.querySelectorAll('*');
12073 for (var i = 0, l = descendants.length; i < l; i++) {
12074 jqLiteRemoveData(descendants[i]);
12079 function jqLiteOff(element, type, fn, unsupported) {
12080 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
12082 var expandoStore = jqLiteExpandoStore(element);
12083 var events = expandoStore && expandoStore.events;
12084 var handle = expandoStore && expandoStore.handle;
12086 if (!handle) return; //no listeners registered
12089 for (type in events) {
12090 if (type !== '$destroy') {
12091 removeEventListenerFn(element, type, handle);
12093 delete events[type];
12097 var removeHandler = function(type) {
12098 var listenerFns = events[type];
12099 if (isDefined(fn)) {
12100 arrayRemove(listenerFns || [], fn);
12102 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
12103 removeEventListenerFn(element, type, handle);
12104 delete events[type];
12108 forEach(type.split(' '), function(type) {
12109 removeHandler(type);
12110 if (MOUSE_EVENT_MAP[type]) {
12111 removeHandler(MOUSE_EVENT_MAP[type]);
12117 function jqLiteRemoveData(element, name) {
12118 var expandoId = element.ng339;
12119 var expandoStore = expandoId && jqCache[expandoId];
12121 if (expandoStore) {
12123 delete expandoStore.data[name];
12127 if (expandoStore.handle) {
12128 if (expandoStore.events.$destroy) {
12129 expandoStore.handle({}, '$destroy');
12131 jqLiteOff(element);
12133 delete jqCache[expandoId];
12134 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
12139 function jqLiteExpandoStore(element, createIfNecessary) {
12140 var expandoId = element.ng339,
12141 expandoStore = expandoId && jqCache[expandoId];
12143 if (createIfNecessary && !expandoStore) {
12144 element.ng339 = expandoId = jqNextId();
12145 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
12148 return expandoStore;
12152 function jqLiteData(element, key, value) {
12153 if (jqLiteAcceptsData(element)) {
12155 var isSimpleSetter = isDefined(value);
12156 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
12157 var massGetter = !key;
12158 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
12159 var data = expandoStore && expandoStore.data;
12161 if (isSimpleSetter) { // data('key', value)
12164 if (massGetter) { // data()
12167 if (isSimpleGetter) { // data('key')
12168 // don't force creation of expandoStore if it doesn't exist yet
12169 return data && data[key];
12170 } else { // mass-setter: data({key1: val1, key2: val2})
12178 function jqLiteHasClass(element, selector) {
12179 if (!element.getAttribute) return false;
12180 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
12181 indexOf(" " + selector + " ") > -1);
12184 function jqLiteRemoveClass(element, cssClasses) {
12185 if (cssClasses && element.setAttribute) {
12186 forEach(cssClasses.split(' '), function(cssClass) {
12187 element.setAttribute('class', trim(
12188 (" " + (element.getAttribute('class') || '') + " ")
12189 .replace(/[\n\t]/g, " ")
12190 .replace(" " + trim(cssClass) + " ", " "))
12196 function jqLiteAddClass(element, cssClasses) {
12197 if (cssClasses && element.setAttribute) {
12198 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
12199 .replace(/[\n\t]/g, " ");
12201 forEach(cssClasses.split(' '), function(cssClass) {
12202 cssClass = trim(cssClass);
12203 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
12204 existingClasses += cssClass + ' ';
12208 element.setAttribute('class', trim(existingClasses));
12213 function jqLiteAddNodes(root, elements) {
12214 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
12218 // if a Node (the most common case)
12219 if (elements.nodeType) {
12220 root[root.length++] = elements;
12222 var length = elements.length;
12224 // if an Array or NodeList and not a Window
12225 if (typeof length === 'number' && elements.window !== elements) {
12227 for (var i = 0; i < length; i++) {
12228 root[root.length++] = elements[i];
12232 root[root.length++] = elements;
12239 function jqLiteController(element, name) {
12240 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
12243 function jqLiteInheritedData(element, name, value) {
12244 // if element is the document object work with the html element instead
12245 // this makes $(document).scope() possible
12246 if (element.nodeType == NODE_TYPE_DOCUMENT) {
12247 element = element.documentElement;
12249 var names = isArray(name) ? name : [name];
12252 for (var i = 0, ii = names.length; i < ii; i++) {
12253 if (isDefined(value = jqLite.data(element, names[i]))) return value;
12256 // If dealing with a document fragment node with a host element, and no parent, use the host
12257 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
12258 // to lookup parent controllers.
12259 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
12263 function jqLiteEmpty(element) {
12264 jqLiteDealoc(element, true);
12265 while (element.firstChild) {
12266 element.removeChild(element.firstChild);
12270 function jqLiteRemove(element, keepData) {
12271 if (!keepData) jqLiteDealoc(element);
12272 var parent = element.parentNode;
12273 if (parent) parent.removeChild(element);
12277 function jqLiteDocumentLoaded(action, win) {
12278 win = win || window;
12279 if (win.document.readyState === 'complete') {
12280 // Force the action to be run async for consistent behavior
12281 // from the action's point of view
12282 // i.e. it will definitely not be in a $apply
12283 win.setTimeout(action);
12285 // No need to unbind this handler as load is only ever called once
12286 jqLite(win).on('load', action);
12290 //////////////////////////////////////////
12291 // Functions which are declared directly.
12292 //////////////////////////////////////////
12293 var JQLitePrototype = JQLite.prototype = {
12294 ready: function(fn) {
12297 function trigger() {
12303 // check if document is already loaded
12304 if (document.readyState === 'complete') {
12305 setTimeout(trigger);
12307 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
12308 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
12310 JQLite(window).on('load', trigger); // fallback to window.onload for others
12314 toString: function() {
12316 forEach(this, function(e) { value.push('' + e);});
12317 return '[' + value.join(', ') + ']';
12320 eq: function(index) {
12321 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
12330 //////////////////////////////////////////
12331 // Functions iterating getter/setters.
12332 // these functions return self on setter and
12334 //////////////////////////////////////////
12335 var BOOLEAN_ATTR = {};
12336 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
12337 BOOLEAN_ATTR[lowercase(value)] = value;
12339 var BOOLEAN_ELEMENTS = {};
12340 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
12341 BOOLEAN_ELEMENTS[value] = true;
12343 var ALIASED_ATTR = {
12344 'ngMinlength': 'minlength',
12345 'ngMaxlength': 'maxlength',
12348 'ngPattern': 'pattern'
12351 function getBooleanAttrName(element, name) {
12352 // check dom last since we will most likely fail on name
12353 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
12355 // booleanAttr is here twice to minimize DOM access
12356 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
12359 function getAliasedAttrName(name) {
12360 return ALIASED_ATTR[name];
12365 removeData: jqLiteRemoveData,
12366 hasData: jqLiteHasData,
12367 cleanData: jqLiteCleanData
12368 }, function(fn, name) {
12374 inheritedData: jqLiteInheritedData,
12376 scope: function(element) {
12377 // Can't use jqLiteData here directly so we stay compatible with jQuery!
12378 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
12381 isolateScope: function(element) {
12382 // Can't use jqLiteData here directly so we stay compatible with jQuery!
12383 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
12386 controller: jqLiteController,
12388 injector: function(element) {
12389 return jqLiteInheritedData(element, '$injector');
12392 removeAttr: function(element, name) {
12393 element.removeAttribute(name);
12396 hasClass: jqLiteHasClass,
12398 css: function(element, name, value) {
12399 name = camelCase(name);
12401 if (isDefined(value)) {
12402 element.style[name] = value;
12404 return element.style[name];
12408 attr: function(element, name, value) {
12409 var nodeType = element.nodeType;
12410 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
12413 var lowercasedName = lowercase(name);
12414 if (BOOLEAN_ATTR[lowercasedName]) {
12415 if (isDefined(value)) {
12417 element[name] = true;
12418 element.setAttribute(name, lowercasedName);
12420 element[name] = false;
12421 element.removeAttribute(lowercasedName);
12424 return (element[name] ||
12425 (element.attributes.getNamedItem(name) || noop).specified)
12429 } else if (isDefined(value)) {
12430 element.setAttribute(name, value);
12431 } else if (element.getAttribute) {
12432 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
12433 // some elements (e.g. Document) don't have get attribute, so return undefined
12434 var ret = element.getAttribute(name, 2);
12435 // normalize non-existing attributes to undefined (as jQuery)
12436 return ret === null ? undefined : ret;
12440 prop: function(element, name, value) {
12441 if (isDefined(value)) {
12442 element[name] = value;
12444 return element[name];
12448 text: (function() {
12452 function getText(element, value) {
12453 if (isUndefined(value)) {
12454 var nodeType = element.nodeType;
12455 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
12457 element.textContent = value;
12461 val: function(element, value) {
12462 if (isUndefined(value)) {
12463 if (element.multiple && nodeName_(element) === 'select') {
12465 forEach(element.options, function(option) {
12466 if (option.selected) {
12467 result.push(option.value || option.text);
12470 return result.length === 0 ? null : result;
12472 return element.value;
12474 element.value = value;
12477 html: function(element, value) {
12478 if (isUndefined(value)) {
12479 return element.innerHTML;
12481 jqLiteDealoc(element, true);
12482 element.innerHTML = value;
12486 }, function(fn, name) {
12488 * Properties: writes return selection, reads return first value
12490 JQLite.prototype[name] = function(arg1, arg2) {
12492 var nodeCount = this.length;
12494 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
12495 // in a way that survives minification.
12496 // jqLiteEmpty takes no arguments but is a setter.
12497 if (fn !== jqLiteEmpty &&
12498 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
12499 if (isObject(arg1)) {
12501 // we are a write, but the object properties are the key/values
12502 for (i = 0; i < nodeCount; i++) {
12503 if (fn === jqLiteData) {
12504 // data() takes the whole object in jQuery
12507 for (key in arg1) {
12508 fn(this[i], key, arg1[key]);
12512 // return self for chaining
12515 // we are a read, so read the first child.
12516 // TODO: do we still need this?
12517 var value = fn.$dv;
12518 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
12519 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
12520 for (var j = 0; j < jj; j++) {
12521 var nodeValue = fn(this[j], arg1, arg2);
12522 value = value ? value + nodeValue : nodeValue;
12527 // we are a write, so apply to all children
12528 for (i = 0; i < nodeCount; i++) {
12529 fn(this[i], arg1, arg2);
12531 // return self for chaining
12537 function createEventHandler(element, events) {
12538 var eventHandler = function(event, type) {
12539 // jQuery specific api
12540 event.isDefaultPrevented = function() {
12541 return event.defaultPrevented;
12544 var eventFns = events[type || event.type];
12545 var eventFnsLength = eventFns ? eventFns.length : 0;
12547 if (!eventFnsLength) return;
12549 if (isUndefined(event.immediatePropagationStopped)) {
12550 var originalStopImmediatePropagation = event.stopImmediatePropagation;
12551 event.stopImmediatePropagation = function() {
12552 event.immediatePropagationStopped = true;
12554 if (event.stopPropagation) {
12555 event.stopPropagation();
12558 if (originalStopImmediatePropagation) {
12559 originalStopImmediatePropagation.call(event);
12564 event.isImmediatePropagationStopped = function() {
12565 return event.immediatePropagationStopped === true;
12568 // Some events have special handlers that wrap the real handler
12569 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
12571 // Copy event handlers in case event handlers array is modified during execution.
12572 if ((eventFnsLength > 1)) {
12573 eventFns = shallowCopy(eventFns);
12576 for (var i = 0; i < eventFnsLength; i++) {
12577 if (!event.isImmediatePropagationStopped()) {
12578 handlerWrapper(element, event, eventFns[i]);
12583 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
12584 // events on `element`
12585 eventHandler.elem = element;
12586 return eventHandler;
12589 function defaultHandlerWrapper(element, event, handler) {
12590 handler.call(element, event);
12593 function specialMouseHandlerWrapper(target, event, handler) {
12594 // Refer to jQuery's implementation of mouseenter & mouseleave
12595 // Read about mouseenter and mouseleave:
12596 // http://www.quirksmode.org/js/events_mouse.html#link8
12597 var related = event.relatedTarget;
12598 // For mousenter/leave call the handler if related is outside the target.
12599 // NB: No relatedTarget if the mouse left/entered the browser window
12600 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
12601 handler.call(target, event);
12605 //////////////////////////////////////////
12606 // Functions iterating traversal.
12607 // These functions chain results into a single
12609 //////////////////////////////////////////
12611 removeData: jqLiteRemoveData,
12613 on: function jqLiteOn(element, type, fn, unsupported) {
12614 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
12616 // Do not add event handlers to non-elements because they will not be cleaned up.
12617 if (!jqLiteAcceptsData(element)) {
12621 var expandoStore = jqLiteExpandoStore(element, true);
12622 var events = expandoStore.events;
12623 var handle = expandoStore.handle;
12626 handle = expandoStore.handle = createEventHandler(element, events);
12629 // http://jsperf.com/string-indexof-vs-split
12630 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
12631 var i = types.length;
12633 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
12634 var eventFns = events[type];
12637 eventFns = events[type] = [];
12638 eventFns.specialHandlerWrapper = specialHandlerWrapper;
12639 if (type !== '$destroy' && !noEventListener) {
12640 addEventListenerFn(element, type, handle);
12649 if (MOUSE_EVENT_MAP[type]) {
12650 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
12651 addHandler(type, undefined, true);
12660 one: function(element, type, fn) {
12661 element = jqLite(element);
12663 //add the listener twice so that when it is called
12664 //you can remove the original function and still be
12665 //able to call element.off(ev, fn) normally
12666 element.on(type, function onFn() {
12667 element.off(type, fn);
12668 element.off(type, onFn);
12670 element.on(type, fn);
12673 replaceWith: function(element, replaceNode) {
12674 var index, parent = element.parentNode;
12675 jqLiteDealoc(element);
12676 forEach(new JQLite(replaceNode), function(node) {
12678 parent.insertBefore(node, index.nextSibling);
12680 parent.replaceChild(node, element);
12686 children: function(element) {
12688 forEach(element.childNodes, function(element) {
12689 if (element.nodeType === NODE_TYPE_ELEMENT) {
12690 children.push(element);
12696 contents: function(element) {
12697 return element.contentDocument || element.childNodes || [];
12700 append: function(element, node) {
12701 var nodeType = element.nodeType;
12702 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
12704 node = new JQLite(node);
12706 for (var i = 0, ii = node.length; i < ii; i++) {
12707 var child = node[i];
12708 element.appendChild(child);
12712 prepend: function(element, node) {
12713 if (element.nodeType === NODE_TYPE_ELEMENT) {
12714 var index = element.firstChild;
12715 forEach(new JQLite(node), function(child) {
12716 element.insertBefore(child, index);
12721 wrap: function(element, wrapNode) {
12722 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
12725 remove: jqLiteRemove,
12727 detach: function(element) {
12728 jqLiteRemove(element, true);
12731 after: function(element, newElement) {
12732 var index = element, parent = element.parentNode;
12733 newElement = new JQLite(newElement);
12735 for (var i = 0, ii = newElement.length; i < ii; i++) {
12736 var node = newElement[i];
12737 parent.insertBefore(node, index.nextSibling);
12742 addClass: jqLiteAddClass,
12743 removeClass: jqLiteRemoveClass,
12745 toggleClass: function(element, selector, condition) {
12747 forEach(selector.split(' '), function(className) {
12748 var classCondition = condition;
12749 if (isUndefined(classCondition)) {
12750 classCondition = !jqLiteHasClass(element, className);
12752 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
12757 parent: function(element) {
12758 var parent = element.parentNode;
12759 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
12762 next: function(element) {
12763 return element.nextElementSibling;
12766 find: function(element, selector) {
12767 if (element.getElementsByTagName) {
12768 return element.getElementsByTagName(selector);
12774 clone: jqLiteClone,
12776 triggerHandler: function(element, event, extraParameters) {
12778 var dummyEvent, eventFnsCopy, handlerArgs;
12779 var eventName = event.type || event;
12780 var expandoStore = jqLiteExpandoStore(element);
12781 var events = expandoStore && expandoStore.events;
12782 var eventFns = events && events[eventName];
12785 // Create a dummy event to pass to the handlers
12787 preventDefault: function() { this.defaultPrevented = true; },
12788 isDefaultPrevented: function() { return this.defaultPrevented === true; },
12789 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
12790 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
12791 stopPropagation: noop,
12796 // If a custom event was provided then extend our dummy event with it
12798 dummyEvent = extend(dummyEvent, event);
12801 // Copy event handlers in case event handlers array is modified during execution.
12802 eventFnsCopy = shallowCopy(eventFns);
12803 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
12805 forEach(eventFnsCopy, function(fn) {
12806 if (!dummyEvent.isImmediatePropagationStopped()) {
12807 fn.apply(element, handlerArgs);
12812 }, function(fn, name) {
12814 * chaining functions
12816 JQLite.prototype[name] = function(arg1, arg2, arg3) {
12819 for (var i = 0, ii = this.length; i < ii; i++) {
12820 if (isUndefined(value)) {
12821 value = fn(this[i], arg1, arg2, arg3);
12822 if (isDefined(value)) {
12823 // any function which returns a value needs to be wrapped
12824 value = jqLite(value);
12827 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
12830 return isDefined(value) ? value : this;
12833 // bind legacy bind/unbind to on/off
12834 JQLite.prototype.bind = JQLite.prototype.on;
12835 JQLite.prototype.unbind = JQLite.prototype.off;
12839 // Provider for private $$jqLite service
12840 function $$jqLiteProvider() {
12841 this.$get = function $$jqLite() {
12842 return extend(JQLite, {
12843 hasClass: function(node, classes) {
12844 if (node.attr) node = node[0];
12845 return jqLiteHasClass(node, classes);
12847 addClass: function(node, classes) {
12848 if (node.attr) node = node[0];
12849 return jqLiteAddClass(node, classes);
12851 removeClass: function(node, classes) {
12852 if (node.attr) node = node[0];
12853 return jqLiteRemoveClass(node, classes);
12860 * Computes a hash of an 'obj'.
12863 * number is number as string
12864 * object is either result of calling $$hashKey function on the object or uniquely generated id,
12865 * that is also assigned to the $$hashKey property of the object.
12868 * @returns {string} hash string such that the same input will have the same hash string.
12869 * The resulting string key is in 'type:hashKey' format.
12871 function hashKey(obj, nextUidFn) {
12872 var key = obj && obj.$$hashKey;
12875 if (typeof key === 'function') {
12876 key = obj.$$hashKey();
12881 var objType = typeof obj;
12882 if (objType == 'function' || (objType == 'object' && obj !== null)) {
12883 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
12885 key = objType + ':' + obj;
12892 * HashMap which can use objects as keys
12894 function HashMap(array, isolatedUid) {
12897 this.nextUid = function() {
12901 forEach(array, this.put, this);
12903 HashMap.prototype = {
12905 * Store key value pair
12906 * @param key key to store can be any type
12907 * @param value value to store can be any type
12909 put: function(key, value) {
12910 this[hashKey(key, this.nextUid)] = value;
12915 * @returns {Object} the value for the key
12917 get: function(key) {
12918 return this[hashKey(key, this.nextUid)];
12922 * Remove the key/value pair
12925 remove: function(key) {
12926 var value = this[key = hashKey(key, this.nextUid)];
12932 var $$HashMapProvider = [function() {
12933 this.$get = [function() {
12941 * @name angular.injector
12945 * Creates an injector object that can be used for retrieving services as well as for
12946 * dependency injection (see {@link guide/di dependency injection}).
12948 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
12949 * {@link angular.module}. The `ng` module must be explicitly added.
12950 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
12951 * disallows argument name annotation inference.
12952 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
12957 * // create an injector
12958 * var $injector = angular.injector(['ng']);
12960 * // use the injector to kick off your application
12961 * // use the type inference to auto inject arguments, or use implicit injection
12962 * $injector.invoke(function($rootScope, $compile, $document) {
12963 * $compile($document)($rootScope);
12964 * $rootScope.$digest();
12968 * Sometimes you want to get access to the injector of a currently running Angular app
12969 * from outside Angular. Perhaps, you want to inject and compile some markup after the
12970 * application has been bootstrapped. You can do this using the extra `injector()` added
12971 * to JQuery/jqLite elements. See {@link angular.element}.
12973 * *This is fairly rare but could be the case if a third party library is injecting the
12976 * In the following example a new block of HTML containing a `ng-controller`
12977 * directive is added to the end of the document body by JQuery. We then compile and link
12978 * it into the current AngularJS scope.
12981 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
12982 * $(document.body).append($div);
12984 * angular.element(document).injector().invoke(function($compile) {
12985 * var scope = angular.element($div).scope();
12986 * $compile($div)(scope);
12997 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
13000 var ARROW_ARG = /^([^\(]+?)=>/;
13001 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
13002 var FN_ARG_SPLIT = /,/;
13003 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
13004 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
13005 var $injectorMinErr = minErr('$injector');
13007 function extractArgs(fn) {
13008 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
13009 args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
13013 function anonFn(fn) {
13014 // For anonymous functions, showing at the very least the function signature can help in
13016 var args = extractArgs(fn);
13018 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
13023 function annotate(fn, strictDi, name) {
13028 if (typeof fn === 'function') {
13029 if (!($inject = fn.$inject)) {
13033 if (!isString(name) || !name) {
13034 name = fn.name || anonFn(fn);
13036 throw $injectorMinErr('strictdi',
13037 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
13039 argDecl = extractArgs(fn);
13040 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
13041 arg.replace(FN_ARG, function(all, underscore, name) {
13042 $inject.push(name);
13046 fn.$inject = $inject;
13048 } else if (isArray(fn)) {
13049 last = fn.length - 1;
13050 assertArgFn(fn[last], 'fn');
13051 $inject = fn.slice(0, last);
13053 assertArgFn(fn, 'fn', true);
13058 ///////////////////////////////////////
13066 * `$injector` is used to retrieve object instances as defined by
13067 * {@link auto.$provide provider}, instantiate types, invoke methods,
13068 * and load modules.
13070 * The following always holds true:
13073 * var $injector = angular.injector();
13074 * expect($injector.get('$injector')).toBe($injector);
13075 * expect($injector.invoke(function($injector) {
13076 * return $injector;
13077 * })).toBe($injector);
13080 * # Injection Function Annotation
13082 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
13083 * following are all valid ways of annotating function with injection arguments and are equivalent.
13086 * // inferred (only works if code not minified/obfuscated)
13087 * $injector.invoke(function(serviceA){});
13090 * function explicit(serviceA) {};
13091 * explicit.$inject = ['serviceA'];
13092 * $injector.invoke(explicit);
13095 * $injector.invoke(['serviceA', function(serviceA){}]);
13100 * In JavaScript calling `toString()` on a function returns the function definition. The definition
13101 * can then be parsed and the function arguments can be extracted. This method of discovering
13102 * annotations is disallowed when the injector is in strict mode.
13103 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
13106 * ## `$inject` Annotation
13107 * By adding an `$inject` property onto a function the injection parameters can be specified.
13110 * As an array of injection names, where the last item in the array is the function to call.
13115 * @name $injector#get
13118 * Return an instance of the service.
13120 * @param {string} name The name of the instance to retrieve.
13121 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
13122 * @return {*} The instance.
13127 * @name $injector#invoke
13130 * Invoke the method and supply the method arguments from the `$injector`.
13132 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
13133 * injected according to the {@link guide/di $inject Annotation} rules.
13134 * @param {Object=} self The `this` for the invoked method.
13135 * @param {Object=} locals Optional object. If preset then any argument names are read from this
13136 * object first, before the `$injector` is consulted.
13137 * @returns {*} the value returned by the invoked `fn` function.
13142 * @name $injector#has
13145 * Allows the user to query if the particular service exists.
13147 * @param {string} name Name of the service to query.
13148 * @returns {boolean} `true` if injector has given service.
13153 * @name $injector#instantiate
13155 * Create a new instance of JS type. The method takes a constructor function, invokes the new
13156 * operator, and supplies all of the arguments to the constructor function as specified by the
13157 * constructor annotation.
13159 * @param {Function} Type Annotated constructor function.
13160 * @param {Object=} locals Optional object. If preset then any argument names are read from this
13161 * object first, before the `$injector` is consulted.
13162 * @returns {Object} new instance of `Type`.
13167 * @name $injector#annotate
13170 * Returns an array of service names which the function is requesting for injection. This API is
13171 * used by the injector to determine which services need to be injected into the function when the
13172 * function is invoked. There are three ways in which the function can be annotated with the needed
13177 * The simplest form is to extract the dependencies from the arguments of the function. This is done
13178 * by converting the function into a string using `toString()` method and extracting the argument
13182 * function MyController($scope, $route) {
13187 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
13190 * You can disallow this method by using strict injection mode.
13192 * This method does not work with code minification / obfuscation. For this reason the following
13193 * annotation strategies are supported.
13195 * # The `$inject` property
13197 * If a function has an `$inject` property and its value is an array of strings, then the strings
13198 * represent names of services to be injected into the function.
13201 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
13204 * // Define function dependencies
13205 * MyController['$inject'] = ['$scope', '$route'];
13208 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
13211 * # The array notation
13213 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
13214 * is very inconvenient. In these situations using the array notation to specify the dependencies in
13215 * a way that survives minification is a better choice:
13218 * // We wish to write this (not minification / obfuscation safe)
13219 * injector.invoke(function($compile, $rootScope) {
13223 * // We are forced to write break inlining
13224 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
13227 * tmpFn.$inject = ['$compile', '$rootScope'];
13228 * injector.invoke(tmpFn);
13230 * // To better support inline function the inline annotation is supported
13231 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
13236 * expect(injector.annotate(
13237 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
13238 * ).toEqual(['$compile', '$rootScope']);
13241 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
13242 * be retrieved as described above.
13244 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
13246 * @returns {Array.<string>} The names of the services which the function requires.
13258 * The {@link auto.$provide $provide} service has a number of methods for registering components
13259 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
13260 * {@link angular.Module}.
13262 * An Angular **service** is a singleton object created by a **service factory**. These **service
13263 * factories** are functions which, in turn, are created by a **service provider**.
13264 * The **service providers** are constructor functions. When instantiated they must contain a
13265 * property called `$get`, which holds the **service factory** function.
13267 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
13268 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
13269 * function to get the instance of the **service**.
13271 * Often services have no configuration options and there is no need to add methods to the service
13272 * provider. The provider will be no more than a constructor function with a `$get` property. For
13273 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
13274 * services without specifying a provider.
13276 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
13277 * {@link auto.$injector $injector}
13278 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
13279 * providers and services.
13280 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
13281 * services, not providers.
13282 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
13283 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
13284 * given factory function.
13285 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
13286 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
13287 * a new object using the given constructor function.
13289 * See the individual methods for more information and examples.
13294 * @name $provide#provider
13297 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
13298 * are constructor functions, whose instances are responsible for "providing" a factory for a
13301 * Service provider names start with the name of the service they provide followed by `Provider`.
13302 * For example, the {@link ng.$log $log} service has a provider called
13303 * {@link ng.$logProvider $logProvider}.
13305 * Service provider objects can have additional methods which allow configuration of the provider
13306 * and its service. Importantly, you can configure what kind of service is created by the `$get`
13307 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
13308 * method {@link ng.$logProvider#debugEnabled debugEnabled}
13309 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
13312 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
13314 * @param {(Object|function())} provider If the provider is:
13316 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
13317 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
13318 * - `Constructor`: a new instance of the provider will be created using
13319 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
13321 * @returns {Object} registered provider instance
13325 * The following example shows how to create a simple event tracking service and register it using
13326 * {@link auto.$provide#provider $provide.provider()}.
13329 * // Define the eventTracker provider
13330 * function EventTrackerProvider() {
13331 * var trackingUrl = '/track';
13333 * // A provider method for configuring where the tracked events should been saved
13334 * this.setTrackingUrl = function(url) {
13335 * trackingUrl = url;
13338 * // The service factory function
13339 * this.$get = ['$http', function($http) {
13340 * var trackedEvents = {};
13342 * // Call this to track an event
13343 * event: function(event) {
13344 * var count = trackedEvents[event] || 0;
13346 * trackedEvents[event] = count;
13349 * // Call this to save the tracked events to the trackingUrl
13350 * save: function() {
13351 * $http.post(trackingUrl, trackedEvents);
13357 * describe('eventTracker', function() {
13360 * beforeEach(module(function($provide) {
13361 * // Register the eventTracker provider
13362 * $provide.provider('eventTracker', EventTrackerProvider);
13365 * beforeEach(module(function(eventTrackerProvider) {
13366 * // Configure eventTracker provider
13367 * eventTrackerProvider.setTrackingUrl('/custom-track');
13370 * it('tracks events', inject(function(eventTracker) {
13371 * expect(eventTracker.event('login')).toEqual(1);
13372 * expect(eventTracker.event('login')).toEqual(2);
13375 * it('saves to the tracking url', inject(function(eventTracker, $http) {
13376 * postSpy = spyOn($http, 'post');
13377 * eventTracker.event('login');
13378 * eventTracker.save();
13379 * expect(postSpy).toHaveBeenCalled();
13380 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
13381 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
13382 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
13390 * @name $provide#factory
13393 * Register a **service factory**, which will be called to return the service instance.
13394 * This is short for registering a service where its provider consists of only a `$get` property,
13395 * which is the given service factory function.
13396 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
13397 * configure your service in a provider.
13399 * @param {string} name The name of the instance.
13400 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
13401 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
13402 * @returns {Object} registered provider instance
13405 * Here is an example of registering a service
13407 * $provide.factory('ping', ['$http', function($http) {
13408 * return function ping() {
13409 * return $http.send('/ping');
13413 * You would then inject and use this service like this:
13415 * someModule.controller('Ctrl', ['ping', function(ping) {
13424 * @name $provide#service
13427 * Register a **service constructor**, which will be invoked with `new` to create the service
13429 * This is short for registering a service where its provider's `$get` property is a factory
13430 * function that returns an instance instantiated by the injector from the service constructor
13433 * Internally it looks a bit like this:
13437 * $get: function() {
13438 * return $injector.instantiate(constructor);
13444 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
13447 * @param {string} name The name of the instance.
13448 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
13449 * that will be instantiated.
13450 * @returns {Object} registered provider instance
13453 * Here is an example of registering a service using
13454 * {@link auto.$provide#service $provide.service(class)}.
13456 * var Ping = function($http) {
13457 * this.$http = $http;
13460 * Ping.$inject = ['$http'];
13462 * Ping.prototype.send = function() {
13463 * return this.$http.get('/ping');
13465 * $provide.service('ping', Ping);
13467 * You would then inject and use this service like this:
13469 * someModule.controller('Ctrl', ['ping', function(ping) {
13478 * @name $provide#value
13481 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
13482 * number, an array, an object or a function. This is short for registering a service where its
13483 * provider's `$get` property is a factory function that takes no arguments and returns the **value
13486 * Value services are similar to constant services, except that they cannot be injected into a
13487 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
13489 * {@link auto.$provide#decorator decorator}.
13491 * @param {string} name The name of the instance.
13492 * @param {*} value The value.
13493 * @returns {Object} registered provider instance
13496 * Here are some examples of creating value services.
13498 * $provide.value('ADMIN_USER', 'admin');
13500 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
13502 * $provide.value('halfOf', function(value) {
13503 * return value / 2;
13511 * @name $provide#constant
13514 * Register a **constant service**, such as a string, a number, an array, an object or a function,
13515 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
13516 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
13517 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
13519 * @param {string} name The name of the constant.
13520 * @param {*} value The constant value.
13521 * @returns {Object} registered instance
13524 * Here a some examples of creating constants:
13526 * $provide.constant('SHARD_HEIGHT', 306);
13528 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
13530 * $provide.constant('double', function(value) {
13531 * return value * 2;
13539 * @name $provide#decorator
13542 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
13543 * intercepts the creation of a service, allowing it to override or modify the behavior of the
13544 * service. The object returned by the decorator may be the original service, or a new service
13545 * object which replaces or wraps and delegates to the original service.
13547 * @param {string} name The name of the service to decorate.
13548 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
13549 * instantiated and should return the decorated service instance. The function is called using
13550 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
13551 * Local injection arguments:
13553 * * `$delegate` - The original service instance, which can be monkey patched, configured,
13554 * decorated or delegated to.
13557 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
13558 * calls to {@link ng.$log#error $log.warn()}.
13560 * $provide.decorator('$log', ['$delegate', function($delegate) {
13561 * $delegate.warn = $delegate.error;
13562 * return $delegate;
13568 function createInjector(modulesToLoad, strictDi) {
13569 strictDi = (strictDi === true);
13570 var INSTANTIATING = {},
13571 providerSuffix = 'Provider',
13573 loadedModules = new HashMap([], true),
13576 provider: supportObject(provider),
13577 factory: supportObject(factory),
13578 service: supportObject(service),
13579 value: supportObject(value),
13580 constant: supportObject(constant),
13581 decorator: decorator
13584 providerInjector = (providerCache.$injector =
13585 createInternalInjector(providerCache, function(serviceName, caller) {
13586 if (angular.isString(caller)) {
13589 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
13591 instanceCache = {},
13592 protoInstanceInjector =
13593 createInternalInjector(instanceCache, function(serviceName, caller) {
13594 var provider = providerInjector.get(serviceName + providerSuffix, caller);
13595 return instanceInjector.invoke(
13596 provider.$get, provider, undefined, serviceName);
13598 instanceInjector = protoInstanceInjector;
13600 providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
13601 var runBlocks = loadModules(modulesToLoad);
13602 instanceInjector = protoInstanceInjector.get('$injector');
13603 instanceInjector.strictDi = strictDi;
13604 forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
13606 return instanceInjector;
13608 ////////////////////////////////////
13610 ////////////////////////////////////
13612 function supportObject(delegate) {
13613 return function(key, value) {
13614 if (isObject(key)) {
13615 forEach(key, reverseParams(delegate));
13617 return delegate(key, value);
13622 function provider(name, provider_) {
13623 assertNotHasOwnProperty(name, 'service');
13624 if (isFunction(provider_) || isArray(provider_)) {
13625 provider_ = providerInjector.instantiate(provider_);
13627 if (!provider_.$get) {
13628 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
13630 return providerCache[name + providerSuffix] = provider_;
13633 function enforceReturnValue(name, factory) {
13634 return function enforcedReturnValue() {
13635 var result = instanceInjector.invoke(factory, this);
13636 if (isUndefined(result)) {
13637 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
13643 function factory(name, factoryFn, enforce) {
13644 return provider(name, {
13645 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
13649 function service(name, constructor) {
13650 return factory(name, ['$injector', function($injector) {
13651 return $injector.instantiate(constructor);
13655 function value(name, val) { return factory(name, valueFn(val), false); }
13657 function constant(name, value) {
13658 assertNotHasOwnProperty(name, 'constant');
13659 providerCache[name] = value;
13660 instanceCache[name] = value;
13663 function decorator(serviceName, decorFn) {
13664 var origProvider = providerInjector.get(serviceName + providerSuffix),
13665 orig$get = origProvider.$get;
13667 origProvider.$get = function() {
13668 var origInstance = instanceInjector.invoke(orig$get, origProvider);
13669 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
13673 ////////////////////////////////////
13675 ////////////////////////////////////
13676 function loadModules(modulesToLoad) {
13677 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
13678 var runBlocks = [], moduleFn;
13679 forEach(modulesToLoad, function(module) {
13680 if (loadedModules.get(module)) return;
13681 loadedModules.put(module, true);
13683 function runInvokeQueue(queue) {
13685 for (i = 0, ii = queue.length; i < ii; i++) {
13686 var invokeArgs = queue[i],
13687 provider = providerInjector.get(invokeArgs[0]);
13689 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
13694 if (isString(module)) {
13695 moduleFn = angularModule(module);
13696 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
13697 runInvokeQueue(moduleFn._invokeQueue);
13698 runInvokeQueue(moduleFn._configBlocks);
13699 } else if (isFunction(module)) {
13700 runBlocks.push(providerInjector.invoke(module));
13701 } else if (isArray(module)) {
13702 runBlocks.push(providerInjector.invoke(module));
13704 assertArgFn(module, 'module');
13707 if (isArray(module)) {
13708 module = module[module.length - 1];
13710 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
13711 // Safari & FF's stack traces don't contain error.message content
13712 // unlike those of Chrome and IE
13713 // So if stack doesn't contain message, we create a new string that contains both.
13714 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
13716 e = e.message + '\n' + e.stack;
13718 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
13719 module, e.stack || e.message || e);
13725 ////////////////////////////////////
13726 // internal Injector
13727 ////////////////////////////////////
13729 function createInternalInjector(cache, factory) {
13731 function getService(serviceName, caller) {
13732 if (cache.hasOwnProperty(serviceName)) {
13733 if (cache[serviceName] === INSTANTIATING) {
13734 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
13735 serviceName + ' <- ' + path.join(' <- '));
13737 return cache[serviceName];
13740 path.unshift(serviceName);
13741 cache[serviceName] = INSTANTIATING;
13742 return cache[serviceName] = factory(serviceName, caller);
13744 if (cache[serviceName] === INSTANTIATING) {
13745 delete cache[serviceName];
13755 function injectionArgs(fn, locals, serviceName) {
13757 $inject = createInjector.$$annotate(fn, strictDi, serviceName);
13759 for (var i = 0, length = $inject.length; i < length; i++) {
13760 var key = $inject[i];
13761 if (typeof key !== 'string') {
13762 throw $injectorMinErr('itkn',
13763 'Incorrect injection token! Expected service name as string, got {0}', key);
13765 args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
13766 getService(key, serviceName));
13771 function isClass(func) {
13772 // IE 9-11 do not support classes and IE9 leaks with the code below.
13776 // Workaround for MS Edge.
13777 // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
13778 return typeof func === 'function'
13779 && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
13782 function invoke(fn, self, locals, serviceName) {
13783 if (typeof locals === 'string') {
13784 serviceName = locals;
13788 var args = injectionArgs(fn, locals, serviceName);
13790 fn = fn[fn.length - 1];
13793 if (!isClass(fn)) {
13794 // http://jsperf.com/angularjs-invoke-apply-vs-switch
13796 return fn.apply(self, args);
13798 args.unshift(null);
13799 return new (Function.prototype.bind.apply(fn, args))();
13804 function instantiate(Type, locals, serviceName) {
13805 // Check if Type is annotated and use just the given function at n-1 as parameter
13806 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
13807 var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
13808 var args = injectionArgs(Type, locals, serviceName);
13809 // Empty object at position 0 is ignored for invocation with `new`, but required.
13810 args.unshift(null);
13811 return new (Function.prototype.bind.apply(ctor, args))();
13817 instantiate: instantiate,
13819 annotate: createInjector.$$annotate,
13820 has: function(name) {
13821 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
13827 createInjector.$$annotate = annotate;
13831 * @name $anchorScrollProvider
13834 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
13835 * {@link ng.$location#hash $location.hash()} changes.
13837 function $AnchorScrollProvider() {
13839 var autoScrollingEnabled = true;
13843 * @name $anchorScrollProvider#disableAutoScrolling
13846 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
13847 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
13848 * Use this method to disable automatic scrolling.
13850 * If automatic scrolling is disabled, one must explicitly call
13851 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
13854 this.disableAutoScrolling = function() {
13855 autoScrollingEnabled = false;
13860 * @name $anchorScroll
13862 * @requires $window
13863 * @requires $location
13864 * @requires $rootScope
13867 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
13868 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
13870 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
13872 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
13873 * match any anchor whenever it changes. This can be disabled by calling
13874 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
13876 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
13877 * vertical scroll-offset (either fixed or dynamic).
13879 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
13880 * {@link ng.$location#hash $location.hash()} will be used.
13882 * @property {(number|function|jqLite)} yOffset
13883 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
13884 * positioned elements at the top of the page, such as navbars, headers etc.
13886 * `yOffset` can be specified in various ways:
13887 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
13888 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
13889 * a number representing the offset (in pixels).<br /><br />
13890 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
13891 * the top of the page to the element's bottom will be used as offset.<br />
13892 * **Note**: The element will be taken into account only as long as its `position` is set to
13893 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
13894 * their height and/or positioning according to the viewport's size.
13897 * <div class="alert alert-warning">
13898 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
13899 * not some child element.
13903 <example module="anchorScrollExample">
13904 <file name="index.html">
13905 <div id="scrollArea" ng-controller="ScrollController">
13906 <a ng-click="gotoBottom()">Go to bottom</a>
13907 <a id="bottom"></a> You're at the bottom!
13910 <file name="script.js">
13911 angular.module('anchorScrollExample', [])
13912 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
13913 function ($scope, $location, $anchorScroll) {
13914 $scope.gotoBottom = function() {
13915 // set the location.hash to the id of
13916 // the element you wish to scroll to.
13917 $location.hash('bottom');
13919 // call $anchorScroll()
13924 <file name="style.css">
13932 margin-top: 2000px;
13938 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
13939 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
13942 <example module="anchorScrollOffsetExample">
13943 <file name="index.html">
13944 <div class="fixed-header" ng-controller="headerCtrl">
13945 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
13949 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
13953 <file name="script.js">
13954 angular.module('anchorScrollOffsetExample', [])
13955 .run(['$anchorScroll', function($anchorScroll) {
13956 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
13958 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
13959 function ($anchorScroll, $location, $scope) {
13960 $scope.gotoAnchor = function(x) {
13961 var newHash = 'anchor' + x;
13962 if ($location.hash() !== newHash) {
13963 // set the $location.hash to `newHash` and
13964 // $anchorScroll will automatically scroll to it
13965 $location.hash('anchor' + x);
13967 // call $anchorScroll() explicitly,
13968 // since $location.hash hasn't changed
13975 <file name="style.css">
13981 border: 2px dashed DarkOrchid;
13982 padding: 10px 10px 200px 10px;
13986 background-color: rgba(0, 0, 0, 0.2);
13989 top: 0; left: 0; right: 0;
13992 .fixed-header > a {
13993 display: inline-block;
13999 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
14000 var document = $window.document;
14002 // Helper function to get first anchor from a NodeList
14003 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
14004 // and working in all supported browsers.)
14005 function getFirstAnchor(list) {
14007 Array.prototype.some.call(list, function(element) {
14008 if (nodeName_(element) === 'a') {
14016 function getYOffset() {
14018 var offset = scroll.yOffset;
14020 if (isFunction(offset)) {
14022 } else if (isElement(offset)) {
14023 var elem = offset[0];
14024 var style = $window.getComputedStyle(elem);
14025 if (style.position !== 'fixed') {
14028 offset = elem.getBoundingClientRect().bottom;
14030 } else if (!isNumber(offset)) {
14037 function scrollTo(elem) {
14039 elem.scrollIntoView();
14041 var offset = getYOffset();
14044 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
14045 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
14046 // top of the viewport.
14048 // IF the number of pixels from the top of `elem` to the end of the page's content is less
14049 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
14050 // way down the page.
14052 // This is often the case for elements near the bottom of the page.
14054 // In such cases we do not need to scroll the whole `offset` up, just the difference between
14055 // the top of the element and the offset, which is enough to align the top of `elem` at the
14056 // desired position.
14057 var elemTop = elem.getBoundingClientRect().top;
14058 $window.scrollBy(0, elemTop - offset);
14061 $window.scrollTo(0, 0);
14065 function scroll(hash) {
14066 hash = isString(hash) ? hash : $location.hash();
14069 // empty hash, scroll to the top of the page
14070 if (!hash) scrollTo(null);
14072 // element with given id
14073 else if ((elm = document.getElementById(hash))) scrollTo(elm);
14075 // first anchor with given name :-D
14076 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
14078 // no element and hash == 'top', scroll to the top of the page
14079 else if (hash === 'top') scrollTo(null);
14082 // does not scroll when user clicks on anchor link that is currently on
14083 // (no url change, no $location.hash() change), browser native does scroll
14084 if (autoScrollingEnabled) {
14085 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
14086 function autoScrollWatchAction(newVal, oldVal) {
14087 // skip the initial scroll if $location.hash is empty
14088 if (newVal === oldVal && newVal === '') return;
14090 jqLiteDocumentLoaded(function() {
14091 $rootScope.$evalAsync(scroll);
14100 var $animateMinErr = minErr('$animate');
14101 var ELEMENT_NODE = 1;
14102 var NG_ANIMATE_CLASSNAME = 'ng-animate';
14104 function mergeClasses(a,b) {
14105 if (!a && !b) return '';
14108 if (isArray(a)) a = a.join(' ');
14109 if (isArray(b)) b = b.join(' ');
14110 return a + ' ' + b;
14113 function extractElementNode(element) {
14114 for (var i = 0; i < element.length; i++) {
14115 var elm = element[i];
14116 if (elm.nodeType === ELEMENT_NODE) {
14122 function splitClasses(classes) {
14123 if (isString(classes)) {
14124 classes = classes.split(' ');
14127 // Use createMap() to prevent class assumptions involving property names in
14128 // Object.prototype
14129 var obj = createMap();
14130 forEach(classes, function(klass) {
14131 // sometimes the split leaves empty string values
14132 // incase extra spaces were applied to the options
14133 if (klass.length) {
14140 // if any other type of options value besides an Object value is
14141 // passed into the $animate.method() animation then this helper code
14142 // will be run which will ignore it. While this patch is not the
14143 // greatest solution to this, a lot of existing plugins depend on
14144 // $animate to either call the callback (< 1.2) or return a promise
14145 // that can be changed. This helper function ensures that the options
14146 // are wiped clean incase a callback function is provided.
14147 function prepareAnimateOptions(options) {
14148 return isObject(options)
14153 var $$CoreAnimateJsProvider = function() {
14154 this.$get = function() {};
14157 // this is prefixed with Core since it conflicts with
14158 // the animateQueueProvider defined in ngAnimate/animateQueue.js
14159 var $$CoreAnimateQueueProvider = function() {
14160 var postDigestQueue = new HashMap();
14161 var postDigestElements = [];
14163 this.$get = ['$$AnimateRunner', '$rootScope',
14164 function($$AnimateRunner, $rootScope) {
14171 push: function(element, event, options, domOperation) {
14172 domOperation && domOperation();
14174 options = options || {};
14175 options.from && element.css(options.from);
14176 options.to && element.css(options.to);
14178 if (options.addClass || options.removeClass) {
14179 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
14182 var runner = new $$AnimateRunner(); // jshint ignore:line
14184 // since there are no animations to run the runner needs to be
14185 // notified that the animation call is complete.
14192 function updateData(data, classes, value) {
14193 var changed = false;
14195 classes = isString(classes) ? classes.split(' ') :
14196 isArray(classes) ? classes : [];
14197 forEach(classes, function(className) {
14200 data[className] = value;
14207 function handleCSSClassChanges() {
14208 forEach(postDigestElements, function(element) {
14209 var data = postDigestQueue.get(element);
14211 var existing = splitClasses(element.attr('class'));
14214 forEach(data, function(status, className) {
14215 var hasClass = !!existing[className];
14216 if (status !== hasClass) {
14218 toAdd += (toAdd.length ? ' ' : '') + className;
14220 toRemove += (toRemove.length ? ' ' : '') + className;
14225 forEach(element, function(elm) {
14226 toAdd && jqLiteAddClass(elm, toAdd);
14227 toRemove && jqLiteRemoveClass(elm, toRemove);
14229 postDigestQueue.remove(element);
14232 postDigestElements.length = 0;
14236 function addRemoveClassesPostDigest(element, add, remove) {
14237 var data = postDigestQueue.get(element) || {};
14239 var classesAdded = updateData(data, add, true);
14240 var classesRemoved = updateData(data, remove, false);
14242 if (classesAdded || classesRemoved) {
14244 postDigestQueue.put(element, data);
14245 postDigestElements.push(element);
14247 if (postDigestElements.length === 1) {
14248 $rootScope.$$postDigest(handleCSSClassChanges);
14257 * @name $animateProvider
14260 * Default implementation of $animate that doesn't perform any animations, instead just
14261 * synchronously performs DOM updates and resolves the returned runner promise.
14263 * In order to enable animations the `ngAnimate` module has to be loaded.
14265 * To see the functional implementation check out `src/ngAnimate/animate.js`.
14267 var $AnimateProvider = ['$provide', function($provide) {
14268 var provider = this;
14270 this.$$registeredAnimations = Object.create(null);
14274 * @name $animateProvider#register
14277 * Registers a new injectable animation factory function. The factory function produces the
14278 * animation object which contains callback functions for each event that is expected to be
14281 * * `eventFn`: `function(element, ... , doneFunction, options)`
14282 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
14283 * on the type of animation additional arguments will be injected into the animation function. The
14284 * list below explains the function signatures for the different animation methods:
14286 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
14287 * - addClass: function(element, addedClasses, doneFunction, options)
14288 * - removeClass: function(element, removedClasses, doneFunction, options)
14289 * - enter, leave, move: function(element, doneFunction, options)
14290 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
14292 * Make sure to trigger the `doneFunction` once the animation is fully complete.
14296 * //enter, leave, move signature
14297 * eventFn : function(element, done, options) {
14298 * //code to run the animation
14299 * //once complete, then run done()
14300 * return function endFunction(wasCancelled) {
14301 * //code to cancel the animation
14307 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
14308 * @param {Function} factory The factory function that will be executed to return the animation
14311 this.register = function(name, factory) {
14312 if (name && name.charAt(0) !== '.') {
14313 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
14316 var key = name + '-animation';
14317 provider.$$registeredAnimations[name.substr(1)] = key;
14318 $provide.factory(key, factory);
14323 * @name $animateProvider#classNameFilter
14326 * Sets and/or returns the CSS class regular expression that is checked when performing
14327 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
14328 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
14329 * When setting the `classNameFilter` value, animations will only be performed on elements
14330 * that successfully match the filter expression. This in turn can boost performance
14331 * for low-powered devices as well as applications containing a lot of structural operations.
14332 * @param {RegExp=} expression The className expression which will be checked against all animations
14333 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
14335 this.classNameFilter = function(expression) {
14336 if (arguments.length === 1) {
14337 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
14338 if (this.$$classNameFilter) {
14339 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
14340 if (reservedRegex.test(this.$$classNameFilter.toString())) {
14341 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
14346 return this.$$classNameFilter;
14349 this.$get = ['$$animateQueue', function($$animateQueue) {
14350 function domInsert(element, parentElement, afterElement) {
14351 // if for some reason the previous element was removed
14352 // from the dom sometime before this code runs then let's
14353 // just stick to using the parent element as the anchor
14354 if (afterElement) {
14355 var afterNode = extractElementNode(afterElement);
14356 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
14357 afterElement = null;
14360 afterElement ? afterElement.after(element) : parentElement.prepend(element);
14366 * @description The $animate service exposes a series of DOM utility methods that provide support
14367 * for animation hooks. The default behavior is the application of DOM operations, however,
14368 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
14369 * to ensure that animation runs with the triggered DOM operation.
14371 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
14372 * included and only when it is active then the animation hooks that `$animate` triggers will be
14373 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
14374 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
14375 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
14377 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
14379 * To learn more about enabling animation support, click here to visit the
14380 * {@link ngAnimate ngAnimate module page}.
14383 // we don't call it directly since non-existant arguments may
14384 // be interpreted as null within the sub enabled function
14389 * @name $animate#on
14391 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
14392 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
14393 * is fired with the following params:
14396 * $animate.on('enter', container,
14397 * function callback(element, phase) {
14398 * // cool we detected an enter animation within the container
14403 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
14404 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
14405 * as well as among its children
14406 * @param {Function} callback the callback function that will be fired when the listener is triggered
14408 * The arguments present in the callback function are:
14409 * * `element` - The captured DOM element that the animation was fired on.
14410 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
14412 on: $$animateQueue.on,
14417 * @name $animate#off
14419 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
14420 * can be used in three different ways depending on the arguments:
14423 * // remove all the animation event listeners listening for `enter`
14424 * $animate.off('enter');
14426 * // remove all the animation event listeners listening for `enter` on the given element and its children
14427 * $animate.off('enter', container);
14429 * // remove the event listener function provided by `callback` that is set
14430 * // to listen for `enter` on the given `container` as well as its children
14431 * $animate.off('enter', container, callback);
14434 * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
14435 * @param {DOMElement=} container the container element the event listener was placed on
14436 * @param {Function=} callback the callback function that was registered as the listener
14438 off: $$animateQueue.off,
14442 * @name $animate#pin
14444 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
14445 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
14446 * element despite being outside the realm of the application or within another application. Say for example if the application
14447 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
14448 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
14449 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
14451 * Note that this feature is only active when the `ngAnimate` module is used.
14453 * @param {DOMElement} element the external element that will be pinned
14454 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
14456 pin: $$animateQueue.pin,
14461 * @name $animate#enabled
14463 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
14464 * function can be called in four ways:
14467 * // returns true or false
14468 * $animate.enabled();
14470 * // changes the enabled state for all animations
14471 * $animate.enabled(false);
14472 * $animate.enabled(true);
14474 * // returns true or false if animations are enabled for an element
14475 * $animate.enabled(element);
14477 * // changes the enabled state for an element and its children
14478 * $animate.enabled(element, true);
14479 * $animate.enabled(element, false);
14482 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
14483 * @param {boolean=} enabled whether or not the animations will be enabled for the element
14485 * @return {boolean} whether or not animations are enabled
14487 enabled: $$animateQueue.enabled,
14491 * @name $animate#cancel
14493 * @description Cancels the provided animation.
14495 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
14497 cancel: function(runner) {
14498 runner.end && runner.end();
14504 * @name $animate#enter
14506 * @description Inserts the element into the DOM either after the `after` element (if provided) or
14507 * as the first child within the `parent` element and then triggers an animation.
14508 * A promise is returned that will be resolved during the next digest once the animation
14511 * @param {DOMElement} element the element which will be inserted into the DOM
14512 * @param {DOMElement} parent the parent element which will append the element as
14513 * a child (so long as the after element is not present)
14514 * @param {DOMElement=} after the sibling element after which the element will be appended
14515 * @param {object=} options an optional collection of options/styles that will be applied to the element
14517 * @return {Promise} the animation callback promise
14519 enter: function(element, parent, after, options) {
14520 parent = parent && jqLite(parent);
14521 after = after && jqLite(after);
14522 parent = parent || after.parent();
14523 domInsert(element, parent, after);
14524 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
14530 * @name $animate#move
14532 * @description Inserts (moves) the element into its new position in the DOM either after
14533 * the `after` element (if provided) or as the first child within the `parent` element
14534 * and then triggers an animation. A promise is returned that will be resolved
14535 * during the next digest once the animation has completed.
14537 * @param {DOMElement} element the element which will be moved into the new DOM position
14538 * @param {DOMElement} parent the parent element which will append the element as
14539 * a child (so long as the after element is not present)
14540 * @param {DOMElement=} after the sibling element after which the element will be appended
14541 * @param {object=} options an optional collection of options/styles that will be applied to the element
14543 * @return {Promise} the animation callback promise
14545 move: function(element, parent, after, options) {
14546 parent = parent && jqLite(parent);
14547 after = after && jqLite(after);
14548 parent = parent || after.parent();
14549 domInsert(element, parent, after);
14550 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
14555 * @name $animate#leave
14557 * @description Triggers an animation and then removes the element from the DOM.
14558 * When the function is called a promise is returned that will be resolved during the next
14559 * digest once the animation has completed.
14561 * @param {DOMElement} element the element which will be removed from the DOM
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 leave: function(element, options) {
14567 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
14574 * @name $animate#addClass
14577 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
14578 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
14579 * animation if element already contains the CSS class or if the class is removed at a later step.
14580 * Note that class-based animations are treated differently compared to structural animations
14581 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14582 * depending if CSS or JavaScript animations are used.
14584 * @param {DOMElement} element the element which the CSS classes will be applied to
14585 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
14586 * @param {object=} options an optional collection of options/styles that will be applied to the element
14588 * @return {Promise} the animation callback promise
14590 addClass: function(element, className, options) {
14591 options = prepareAnimateOptions(options);
14592 options.addClass = mergeClasses(options.addclass, className);
14593 return $$animateQueue.push(element, 'addClass', options);
14598 * @name $animate#removeClass
14601 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
14602 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
14603 * animation if element does not contain the CSS class or if the class is added at a later step.
14604 * Note that class-based animations are treated differently compared to structural animations
14605 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14606 * depending if CSS or JavaScript animations are used.
14608 * @param {DOMElement} element the element which the CSS classes will be applied to
14609 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
14610 * @param {object=} options an optional collection of options/styles that will be applied to the element
14612 * @return {Promise} the animation callback promise
14614 removeClass: function(element, className, options) {
14615 options = prepareAnimateOptions(options);
14616 options.removeClass = mergeClasses(options.removeClass, className);
14617 return $$animateQueue.push(element, 'removeClass', options);
14622 * @name $animate#setClass
14625 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
14626 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
14627 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
14628 * passed. Note that class-based animations are treated differently compared to structural animations
14629 * (like enter, move and leave) since the CSS classes may be added/removed at different points
14630 * depending if CSS or JavaScript animations are used.
14632 * @param {DOMElement} element the element which the CSS classes will be applied to
14633 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
14634 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
14635 * @param {object=} options an optional collection of options/styles that will be applied to the element
14637 * @return {Promise} the animation callback promise
14639 setClass: function(element, add, remove, options) {
14640 options = prepareAnimateOptions(options);
14641 options.addClass = mergeClasses(options.addClass, add);
14642 options.removeClass = mergeClasses(options.removeClass, remove);
14643 return $$animateQueue.push(element, 'setClass', options);
14648 * @name $animate#animate
14651 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
14652 * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
14653 * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
14654 * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
14655 * style in `to`, the style in `from` is applied immediately, and no animation is run.
14656 * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
14657 * method (or as part of the `options` parameter):
14660 * ngModule.animation('.my-inline-animation', function() {
14662 * animate : function(element, from, to, done, options) {
14670 * @param {DOMElement} element the element which the CSS styles will be applied to
14671 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
14672 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
14673 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
14674 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
14675 * (Note that if no animation is detected then this value will not be applied to the element.)
14676 * @param {object=} options an optional collection of options/styles that will be applied to the element
14678 * @return {Promise} the animation callback promise
14680 animate: function(element, from, to, className, options) {
14681 options = prepareAnimateOptions(options);
14682 options.from = options.from ? extend(options.from, from) : from;
14683 options.to = options.to ? extend(options.to, to) : to;
14685 className = className || 'ng-inline-animate';
14686 options.tempClasses = mergeClasses(options.tempClasses, className);
14687 return $$animateQueue.push(element, 'animate', options);
14693 var $$AnimateAsyncRunFactoryProvider = function() {
14694 this.$get = ['$$rAF', function($$rAF) {
14695 var waitQueue = [];
14697 function waitForTick(fn) {
14698 waitQueue.push(fn);
14699 if (waitQueue.length > 1) return;
14701 for (var i = 0; i < waitQueue.length; i++) {
14708 return function() {
14709 var passed = false;
14710 waitForTick(function() {
14713 return function(callback) {
14714 passed ? callback() : waitForTick(callback);
14720 var $$AnimateRunnerFactoryProvider = function() {
14721 this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
14722 function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
14724 var INITIAL_STATE = 0;
14725 var DONE_PENDING_STATE = 1;
14726 var DONE_COMPLETE_STATE = 2;
14728 AnimateRunner.chain = function(chain, callback) {
14733 if (index === chain.length) {
14738 chain[index](function(response) {
14739 if (response === false) {
14749 AnimateRunner.all = function(runners, callback) {
14752 forEach(runners, function(runner) {
14753 runner.done(onProgress);
14756 function onProgress(response) {
14757 status = status && response;
14758 if (++count === runners.length) {
14764 function AnimateRunner(host) {
14765 this.setHost(host);
14767 var rafTick = $$animateAsyncRun();
14768 var timeoutTick = function(fn) {
14769 $timeout(fn, 0, false);
14772 this._doneCallbacks = [];
14773 this._tick = function(fn) {
14774 var doc = $document[0];
14776 // the document may not be ready or attached
14777 // to the module for some internal tests
14778 if (doc && doc.hidden) {
14787 AnimateRunner.prototype = {
14788 setHost: function(host) {
14789 this.host = host || {};
14792 done: function(fn) {
14793 if (this._state === DONE_COMPLETE_STATE) {
14796 this._doneCallbacks.push(fn);
14802 getPromise: function() {
14803 if (!this.promise) {
14805 this.promise = $q(function(resolve, reject) {
14806 self.done(function(status) {
14807 status === false ? reject() : resolve();
14811 return this.promise;
14814 then: function(resolveHandler, rejectHandler) {
14815 return this.getPromise().then(resolveHandler, rejectHandler);
14818 'catch': function(handler) {
14819 return this.getPromise()['catch'](handler);
14822 'finally': function(handler) {
14823 return this.getPromise()['finally'](handler);
14826 pause: function() {
14827 if (this.host.pause) {
14832 resume: function() {
14833 if (this.host.resume) {
14834 this.host.resume();
14839 if (this.host.end) {
14842 this._resolve(true);
14845 cancel: function() {
14846 if (this.host.cancel) {
14847 this.host.cancel();
14849 this._resolve(false);
14852 complete: function(response) {
14854 if (self._state === INITIAL_STATE) {
14855 self._state = DONE_PENDING_STATE;
14856 self._tick(function() {
14857 self._resolve(response);
14862 _resolve: function(response) {
14863 if (this._state !== DONE_COMPLETE_STATE) {
14864 forEach(this._doneCallbacks, function(fn) {
14867 this._doneCallbacks.length = 0;
14868 this._state = DONE_COMPLETE_STATE;
14873 return AnimateRunner;
14879 * @name $animateCss
14883 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
14884 * then the `$animateCss` service will actually perform animations.
14886 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
14888 var $CoreAnimateCssProvider = function() {
14889 this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
14891 return function(element, initialOptions) {
14892 // all of the animation functions should create
14893 // a copy of the options data, however, if a
14894 // parent service has already created a copy then
14895 // we should stick to using that
14896 var options = initialOptions || {};
14897 if (!options.$$prepared) {
14898 options = copy(options);
14901 // there is no point in applying the styles since
14902 // there is no animation that goes on at all in
14903 // this version of $animateCss.
14904 if (options.cleanupStyles) {
14905 options.from = options.to = null;
14908 if (options.from) {
14909 element.css(options.from);
14910 options.from = null;
14913 /* jshint newcap: false */
14914 var closed, runner = new $$AnimateRunner();
14922 applyAnimationContents();
14931 function applyAnimationContents() {
14932 if (options.addClass) {
14933 element.addClass(options.addClass);
14934 options.addClass = null;
14936 if (options.removeClass) {
14937 element.removeClass(options.removeClass);
14938 options.removeClass = null;
14941 element.css(options.to);
14949 /* global stripHash: true */
14952 * ! This is a private undocumented service !
14957 * This object has two goals:
14959 * - hide all the global state in the browser caused by the window object
14960 * - abstract away all the browser specific features and inconsistencies
14962 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
14963 * service, which can be used for convenient testing of the application without the interaction with
14964 * the real browser apis.
14967 * @param {object} window The global window object.
14968 * @param {object} document jQuery wrapped document.
14969 * @param {object} $log window.console or an object with the same interface.
14970 * @param {object} $sniffer $sniffer service
14972 function Browser(window, document, $log, $sniffer) {
14974 rawDocument = document[0],
14975 location = window.location,
14976 history = window.history,
14977 setTimeout = window.setTimeout,
14978 clearTimeout = window.clearTimeout,
14979 pendingDeferIds = {};
14981 self.isMock = false;
14983 var outstandingRequestCount = 0;
14984 var outstandingRequestCallbacks = [];
14986 // TODO(vojta): remove this temporary api
14987 self.$$completeOutstandingRequest = completeOutstandingRequest;
14988 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
14991 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
14992 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
14994 function completeOutstandingRequest(fn) {
14996 fn.apply(null, sliceArgs(arguments, 1));
14998 outstandingRequestCount--;
14999 if (outstandingRequestCount === 0) {
15000 while (outstandingRequestCallbacks.length) {
15002 outstandingRequestCallbacks.pop()();
15011 function getHash(url) {
15012 var index = url.indexOf('#');
15013 return index === -1 ? '' : url.substr(index);
15018 * Note: this method is used only by scenario runner
15019 * TODO(vojta): prefix this method with $$ ?
15020 * @param {function()} callback Function that will be called when no outstanding request
15022 self.notifyWhenNoOutstandingRequests = function(callback) {
15023 if (outstandingRequestCount === 0) {
15026 outstandingRequestCallbacks.push(callback);
15030 //////////////////////////////////////////////////////////////
15032 //////////////////////////////////////////////////////////////
15034 var cachedState, lastHistoryState,
15035 lastBrowserUrl = location.href,
15036 baseElement = document.find('base'),
15037 pendingLocation = null;
15040 lastHistoryState = cachedState;
15043 * @name $browser#url
15047 * Without any argument, this method just returns current value of location.href.
15050 * With at least one argument, this method sets url to new value.
15051 * If html5 history api supported, pushState/replaceState is used, otherwise
15052 * location.href/location.replace is used.
15053 * Returns its own instance to allow chaining
15055 * NOTE: this api is intended for use only by the $location service. Please use the
15056 * {@link ng.$location $location service} to change url.
15058 * @param {string} url New url (when used as setter)
15059 * @param {boolean=} replace Should new url replace current history record?
15060 * @param {object=} state object to use with pushState/replaceState
15062 self.url = function(url, replace, state) {
15063 // In modern browsers `history.state` is `null` by default; treating it separately
15064 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
15065 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
15066 if (isUndefined(state)) {
15070 // Android Browser BFCache causes location, history reference to become stale.
15071 if (location !== window.location) location = window.location;
15072 if (history !== window.history) history = window.history;
15076 var sameState = lastHistoryState === state;
15078 // Don't change anything if previous and current URLs and states match. This also prevents
15079 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
15080 // See https://github.com/angular/angular.js/commit/ffb2701
15081 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
15084 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
15085 lastBrowserUrl = url;
15086 lastHistoryState = state;
15087 // Don't use history API if only the hash changed
15088 // due to a bug in IE10/IE11 which leads
15089 // to not firing a `hashchange` nor `popstate` event
15090 // in some cases (see #9143).
15091 if ($sniffer.history && (!sameBase || !sameState)) {
15092 history[replace ? 'replaceState' : 'pushState'](state, '', url);
15094 // Do the assignment again so that those two variables are referentially identical.
15095 lastHistoryState = cachedState;
15097 if (!sameBase || pendingLocation) {
15098 pendingLocation = url;
15101 location.replace(url);
15102 } else if (!sameBase) {
15103 location.href = url;
15105 location.hash = getHash(url);
15107 if (location.href !== url) {
15108 pendingLocation = url;
15114 // - pendingLocation is needed as browsers don't allow to read out
15115 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
15116 // https://openradar.appspot.com/22186109).
15117 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
15118 return pendingLocation || location.href.replace(/%27/g,"'");
15123 * @name $browser#state
15126 * This method is a getter.
15128 * Return history.state or null if history.state is undefined.
15130 * @returns {object} state
15132 self.state = function() {
15133 return cachedState;
15136 var urlChangeListeners = [],
15137 urlChangeInit = false;
15139 function cacheStateAndFireUrlChange() {
15140 pendingLocation = null;
15145 function getCurrentState() {
15147 return history.state;
15149 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
15153 // This variable should be used *only* inside the cacheState function.
15154 var lastCachedState = null;
15155 function cacheState() {
15156 // This should be the only place in $browser where `history.state` is read.
15157 cachedState = getCurrentState();
15158 cachedState = isUndefined(cachedState) ? null : cachedState;
15160 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
15161 if (equals(cachedState, lastCachedState)) {
15162 cachedState = lastCachedState;
15164 lastCachedState = cachedState;
15167 function fireUrlChange() {
15168 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
15172 lastBrowserUrl = self.url();
15173 lastHistoryState = cachedState;
15174 forEach(urlChangeListeners, function(listener) {
15175 listener(self.url(), cachedState);
15180 * @name $browser#onUrlChange
15183 * Register callback function that will be called, when url changes.
15185 * It's only called when the url is changed from outside of angular:
15186 * - user types different url into address bar
15187 * - user clicks on history (forward/back) button
15188 * - user clicks on a link
15190 * It's not called when url is changed by $browser.url() method
15192 * The listener gets called with new url as parameter.
15194 * NOTE: this api is intended for use only by the $location service. Please use the
15195 * {@link ng.$location $location service} to monitor url changes in angular apps.
15197 * @param {function(string)} listener Listener function to be called when url changes.
15198 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
15200 self.onUrlChange = function(callback) {
15201 // TODO(vojta): refactor to use node's syntax for events
15202 if (!urlChangeInit) {
15203 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
15204 // don't fire popstate when user change the address bar and don't fire hashchange when url
15205 // changed by push/replaceState
15207 // html5 history api - popstate event
15208 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
15209 // hashchange event
15210 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
15212 urlChangeInit = true;
15215 urlChangeListeners.push(callback);
15221 * Remove popstate and hashchange handler from window.
15223 * NOTE: this api is intended for use only by $rootScope.
15225 self.$$applicationDestroyed = function() {
15226 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
15230 * Checks whether the url has changed outside of Angular.
15231 * Needs to be exported to be able to check for changes that have been done in sync,
15232 * as hashchange/popstate events fire in async.
15234 self.$$checkUrlChange = fireUrlChange;
15236 //////////////////////////////////////////////////////////////
15238 //////////////////////////////////////////////////////////////
15241 * @name $browser#baseHref
15244 * Returns current <base href>
15245 * (always relative - without domain)
15247 * @returns {string} The current base href
15249 self.baseHref = function() {
15250 var href = baseElement.attr('href');
15251 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
15255 * @name $browser#defer
15256 * @param {function()} fn A function, who's execution should be deferred.
15257 * @param {number=} [delay=0] of milliseconds to defer the function execution.
15258 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
15261 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
15263 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
15264 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
15265 * via `$browser.defer.flush()`.
15268 self.defer = function(fn, delay) {
15270 outstandingRequestCount++;
15271 timeoutId = setTimeout(function() {
15272 delete pendingDeferIds[timeoutId];
15273 completeOutstandingRequest(fn);
15275 pendingDeferIds[timeoutId] = true;
15281 * @name $browser#defer.cancel
15284 * Cancels a deferred task identified with `deferId`.
15286 * @param {*} deferId Token returned by the `$browser.defer` function.
15287 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
15290 self.defer.cancel = function(deferId) {
15291 if (pendingDeferIds[deferId]) {
15292 delete pendingDeferIds[deferId];
15293 clearTimeout(deferId);
15294 completeOutstandingRequest(noop);
15302 function $BrowserProvider() {
15303 this.$get = ['$window', '$log', '$sniffer', '$document',
15304 function($window, $log, $sniffer, $document) {
15305 return new Browser($window, $document, $log, $sniffer);
15311 * @name $cacheFactory
15314 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
15319 * var cache = $cacheFactory('cacheId');
15320 * expect($cacheFactory.get('cacheId')).toBe(cache);
15321 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
15323 * cache.put("key", "value");
15324 * cache.put("another key", "another value");
15326 * // We've specified no options on creation
15327 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
15332 * @param {string} cacheId Name or id of the newly created cache.
15333 * @param {object=} options Options object that specifies the cache behavior. Properties:
15335 * - `{number=}` `capacity` — turns the cache into LRU cache.
15337 * @returns {object} Newly created cache object with the following set of methods:
15339 * - `{object}` `info()` — Returns id, size, and options of cache.
15340 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
15342 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
15343 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
15344 * - `{void}` `removeAll()` — Removes all cached values.
15345 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
15348 <example module="cacheExampleApp">
15349 <file name="index.html">
15350 <div ng-controller="CacheController">
15351 <input ng-model="newCacheKey" placeholder="Key">
15352 <input ng-model="newCacheValue" placeholder="Value">
15353 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
15355 <p ng-if="keys.length">Cached Values</p>
15356 <div ng-repeat="key in keys">
15357 <span ng-bind="key"></span>
15359 <b ng-bind="cache.get(key)"></b>
15363 <div ng-repeat="(key, value) in cache.info()">
15364 <span ng-bind="key"></span>
15366 <b ng-bind="value"></b>
15370 <file name="script.js">
15371 angular.module('cacheExampleApp', []).
15372 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
15374 $scope.cache = $cacheFactory('cacheId');
15375 $scope.put = function(key, value) {
15376 if (angular.isUndefined($scope.cache.get(key))) {
15377 $scope.keys.push(key);
15379 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
15383 <file name="style.css">
15385 margin: 10px 0 3px;
15390 function $CacheFactoryProvider() {
15392 this.$get = function() {
15395 function cacheFactory(cacheId, options) {
15396 if (cacheId in caches) {
15397 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
15401 stats = extend({}, options, {id: cacheId}),
15402 data = createMap(),
15403 capacity = (options && options.capacity) || Number.MAX_VALUE,
15404 lruHash = createMap(),
15410 * @name $cacheFactory.Cache
15413 * A cache object used to store and retrieve data, primarily used by
15414 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
15415 * templates and other data.
15418 * angular.module('superCache')
15419 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
15420 * return $cacheFactory('super-cache');
15427 * it('should behave like a cache', inject(function(superCache) {
15428 * superCache.put('key', 'value');
15429 * superCache.put('another key', 'another value');
15431 * expect(superCache.info()).toEqual({
15432 * id: 'super-cache',
15436 * superCache.remove('another key');
15437 * expect(superCache.get('another key')).toBeUndefined();
15439 * superCache.removeAll();
15440 * expect(superCache.info()).toEqual({
15441 * id: 'super-cache',
15447 return caches[cacheId] = {
15451 * @name $cacheFactory.Cache#put
15455 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
15456 * retrieved later, and incrementing the size of the cache if the key was not already
15457 * present in the cache. If behaving like an LRU cache, it will also remove stale
15458 * entries from the set.
15460 * It will not insert undefined values into the cache.
15462 * @param {string} key the key under which the cached data is stored.
15463 * @param {*} value the value to store alongside the key. If it is undefined, the key
15464 * will not be stored.
15465 * @returns {*} the value stored.
15467 put: function(key, value) {
15468 if (isUndefined(value)) return;
15469 if (capacity < Number.MAX_VALUE) {
15470 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
15475 if (!(key in data)) size++;
15478 if (size > capacity) {
15479 this.remove(staleEnd.key);
15487 * @name $cacheFactory.Cache#get
15491 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
15493 * @param {string} key the key of the data to be retrieved
15494 * @returns {*} the value stored.
15496 get: function(key) {
15497 if (capacity < Number.MAX_VALUE) {
15498 var lruEntry = lruHash[key];
15500 if (!lruEntry) return;
15511 * @name $cacheFactory.Cache#remove
15515 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
15517 * @param {string} key the key of the entry to be removed
15519 remove: function(key) {
15520 if (capacity < Number.MAX_VALUE) {
15521 var lruEntry = lruHash[key];
15523 if (!lruEntry) return;
15525 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
15526 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
15527 link(lruEntry.n,lruEntry.p);
15529 delete lruHash[key];
15532 if (!(key in data)) return;
15541 * @name $cacheFactory.Cache#removeAll
15545 * Clears the cache object of any entries.
15547 removeAll: function() {
15548 data = createMap();
15550 lruHash = createMap();
15551 freshEnd = staleEnd = null;
15557 * @name $cacheFactory.Cache#destroy
15561 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
15562 * removing it from the {@link $cacheFactory $cacheFactory} set.
15564 destroy: function() {
15568 delete caches[cacheId];
15574 * @name $cacheFactory.Cache#info
15578 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
15580 * @returns {object} an object with the following properties:
15582 * <li>**id**: the id of the cache instance</li>
15583 * <li>**size**: the number of entries kept in the cache instance</li>
15584 * <li>**...**: any additional properties from the options object when creating the
15589 return extend({}, stats, {size: size});
15595 * makes the `entry` the freshEnd of the LRU linked list
15597 function refresh(entry) {
15598 if (entry != freshEnd) {
15601 } else if (staleEnd == entry) {
15602 staleEnd = entry.n;
15605 link(entry.n, entry.p);
15606 link(entry, freshEnd);
15614 * bidirectionally links two entries of the LRU linked list
15616 function link(nextEntry, prevEntry) {
15617 if (nextEntry != prevEntry) {
15618 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
15619 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
15627 * @name $cacheFactory#info
15630 * Get information about all the caches that have been created
15632 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
15634 cacheFactory.info = function() {
15636 forEach(caches, function(cache, cacheId) {
15637 info[cacheId] = cache.info();
15645 * @name $cacheFactory#get
15648 * Get access to a cache object by the `cacheId` used when it was created.
15650 * @param {string} cacheId Name or id of a cache to access.
15651 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
15653 cacheFactory.get = function(cacheId) {
15654 return caches[cacheId];
15658 return cacheFactory;
15664 * @name $templateCache
15667 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
15668 * can load templates directly into the cache in a `script` tag, or by consuming the
15669 * `$templateCache` service directly.
15671 * Adding via the `script` tag:
15674 * <script type="text/ng-template" id="templateId.html">
15675 * <p>This is the content of the template</p>
15679 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
15680 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
15681 * element with ng-app attribute), otherwise the template will be ignored.
15683 * Adding via the `$templateCache` service:
15686 * var myApp = angular.module('myApp', []);
15687 * myApp.run(function($templateCache) {
15688 * $templateCache.put('templateId.html', 'This is the content of the template');
15692 * To retrieve the template later, simply use it in your HTML:
15694 * <div ng-include=" 'templateId.html' "></div>
15697 * or get it via Javascript:
15699 * $templateCache.get('templateId.html')
15702 * See {@link ng.$cacheFactory $cacheFactory}.
15705 function $TemplateCacheProvider() {
15706 this.$get = ['$cacheFactory', function($cacheFactory) {
15707 return $cacheFactory('templates');
15711 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15712 * Any commits to this file should be reviewed with security in mind. *
15713 * Changes to this file can potentially create security vulnerabilities. *
15714 * An approval from 2 Core members with history of modifying *
15715 * this file is required. *
15717 * Does the change somehow allow for arbitrary javascript to be executed? *
15718 * Or allows for someone to change the prototype of built-in objects? *
15719 * Or gives undesired access to variables likes document or window? *
15720 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15722 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
15724 * DOM-related variables:
15726 * - "node" - DOM Node
15727 * - "element" - DOM Element or Node
15728 * - "$node" or "$element" - jqLite-wrapped node or element
15731 * Compiler related stuff:
15733 * - "linkFn" - linking fn of a single directive
15734 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
15735 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
15736 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
15746 * Compiles an HTML string or DOM into a template and produces a template function, which
15747 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
15749 * The compilation is a process of walking the DOM tree and matching DOM elements to
15750 * {@link ng.$compileProvider#directive directives}.
15752 * <div class="alert alert-warning">
15753 * **Note:** This document is an in-depth reference of all directive options.
15754 * For a gentle introduction to directives with examples of common use cases,
15755 * see the {@link guide/directive directive guide}.
15758 * ## Comprehensive Directive API
15760 * There are many different options for a directive.
15762 * The difference resides in the return value of the factory function.
15763 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
15764 * or just the `postLink` function (all other properties will have the default values).
15766 * <div class="alert alert-success">
15767 * **Best Practice:** It's recommended to use the "directive definition object" form.
15770 * Here's an example directive declared with a Directive Definition Object:
15773 * var myModule = angular.module(...);
15775 * myModule.directive('directiveName', function factory(injectables) {
15776 * var directiveDefinitionObject = {
15778 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
15780 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
15781 * transclude: false,
15783 * templateNamespace: 'html',
15785 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
15786 * controllerAs: 'stringIdentifier',
15787 * bindToController: false,
15788 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
15789 * compile: function compile(tElement, tAttrs, transclude) {
15791 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
15792 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
15795 * // return function postLink( ... ) { ... }
15799 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
15800 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
15803 * // link: function postLink( ... ) { ... }
15805 * return directiveDefinitionObject;
15809 * <div class="alert alert-warning">
15810 * **Note:** Any unspecified options will use the default value. You can see the default values below.
15813 * Therefore the above can be simplified as:
15816 * var myModule = angular.module(...);
15818 * myModule.directive('directiveName', function factory(injectables) {
15819 * var directiveDefinitionObject = {
15820 * link: function postLink(scope, iElement, iAttrs) { ... }
15822 * return directiveDefinitionObject;
15824 * // return function postLink(scope, iElement, iAttrs) { ... }
15830 * ### Directive Definition Object
15832 * The directive definition object provides instructions to the {@link ng.$compile
15833 * compiler}. The attributes are:
15835 * #### `multiElement`
15836 * When this property is set to true, the HTML compiler will collect DOM nodes between
15837 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
15838 * together as the directive elements. It is recommended that this feature be used on directives
15839 * which are not strictly behavioral (such as {@link ngClick}), and which
15840 * do not manipulate or replace child nodes (such as {@link ngInclude}).
15843 * When there are multiple directives defined on a single DOM element, sometimes it
15844 * is necessary to specify the order in which the directives are applied. The `priority` is used
15845 * to sort the directives before their `compile` functions get called. Priority is defined as a
15846 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
15847 * are also run in priority order, but post-link functions are run in reverse order. The order
15848 * of directives with the same priority is undefined. The default priority is `0`.
15851 * If set to true then the current `priority` will be the last set of directives
15852 * which will execute (any directives at the current priority will still execute
15853 * as the order of execution on same `priority` is undefined). Note that expressions
15854 * and other directives used in the directive's template will also be excluded from execution.
15857 * The scope property can be `true`, an object or a falsy value:
15859 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
15861 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
15862 * the directive's element. If multiple directives on the same element request a new scope,
15863 * only one new scope is created. The new scope rule does not apply for the root of the template
15864 * since the root of the template always gets a new scope.
15866 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
15867 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
15868 * scope. This is useful when creating reusable components, which should not accidentally read or modify
15869 * data in the parent scope.
15871 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
15872 * directive's element. These local properties are useful for aliasing values for templates. The keys in
15873 * the object hash map to the name of the property on the isolate scope; the values define how the property
15874 * is bound to the parent scope, via matching attributes on the directive's element:
15876 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
15877 * always a string since DOM attributes are strings. If no `attr` name is specified then the
15878 * attribute name is assumed to be the same as the local name. Given `<my-component
15879 * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
15880 * the directive's scope property `localName` will reflect the interpolated value of `hello
15881 * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
15882 * scope. The `name` is read from the parent scope (not the directive's scope).
15884 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
15885 * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
15886 * If no `attr` name is specified then the attribute name is assumed to be the same as the local
15887 * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
15888 * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
15889 * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
15890 * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
15891 * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
15892 * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
15893 * will be thrown upon discovering changes to the local value, since it will be impossible to sync
15894 * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
15895 * method is used for tracking changes, and the equality check is based on object identity.
15896 * However, if an object literal or an array literal is passed as the binding expression, the
15897 * equality check is done by value (using the {@link angular.equals} function). It's also possible
15898 * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
15899 * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
15901 * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
15902 * expression passed via the attribute `attr`. The expression is evaluated in the context of the
15903 * parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
15904 * local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
15906 * For example, given `<my-component my-attr="parentModel">` and directive definition of
15907 * `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
15908 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
15909 * in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
15911 * 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
15912 * sets the same value. That means if your bound value is an object, changes to its properties
15913 * in the isolated scope will be reflected in the parent scope (because both reference the same object).
15914 * 2. one-way binding watches changes to the **identity** of the parent value. That means the
15915 * {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
15916 * to the value has changed. In most cases, this should not be of concern, but can be important
15917 * to know if you one-way bind to an object, and then replace that object in the isolated scope.
15918 * If you now change a property of the object in your parent scope, the change will not be
15919 * propagated to the isolated scope, because the identity of the object on the parent scope
15920 * has not changed. Instead you must assign a new object.
15922 * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
15923 * back to the parent. However, it does not make this completely impossible.
15925 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
15926 * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
15927 * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
15928 * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
15929 * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
15930 * via an expression to the parent scope. This can be done by passing a map of local variable names
15931 * and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
15932 * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
15934 * In general it's possible to apply more than one directive to one element, but there might be limitations
15935 * depending on the type of scope required by the directives. The following points will help explain these limitations.
15936 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
15938 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
15939 * * **child scope** + **no scope** => Both directives will share one single child scope
15940 * * **child scope** + **child scope** => Both directives will share one single child scope
15941 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
15942 * its parent's scope
15943 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
15944 * be applied to the same element.
15945 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
15946 * cannot be applied to the same element.
15949 * #### `bindToController`
15950 * This property is used to bind scope properties directly to the controller. It can be either
15951 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
15952 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
15953 * definition: `controller: 'myCtrl as myAlias'`.
15955 * When an isolate scope is used for a directive (see above), `bindToController: true` will
15956 * allow a component to have its properties bound to the controller, rather than to scope.
15958 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
15959 * properties. You can access these bindings once they have been initialized by providing a controller method called
15960 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
15963 * <div class="alert alert-warning">
15964 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
15965 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
15966 * code that relies upon bindings inside a `$onInit` method on the controller, instead.
15969 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
15970 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
15971 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
15972 * scope (useful for component directives).
15974 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
15977 * #### `controller`
15978 * Controller constructor function. The controller is instantiated before the
15979 * pre-linking phase and can be accessed by other directives (see
15980 * `require` attribute). This allows the directives to communicate with each other and augment
15981 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
15983 * * `$scope` - Current scope associated with the element
15984 * * `$element` - Current element
15985 * * `$attrs` - Current attributes object for the element
15986 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
15987 * `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
15988 * * `scope`: (optional) override the scope.
15989 * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
15990 * * `futureParentElement` (optional):
15991 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
15992 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
15993 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
15994 * and when the `cloneLinkinFn` is passed,
15995 * as those elements need to created and cloned in a special way when they are defined outside their
15996 * usual containers (e.g. like `<svg>`).
15997 * * See also the `directive.templateNamespace` property.
15998 * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
15999 * then the default translusion is provided.
16000 * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
16001 * `true` if the specified slot contains content (i.e. one or more DOM nodes).
16003 * The controller can provide the following methods that act as life-cycle hooks:
16004 * * `$onInit` - Called on each controller after all the controllers on an element have been constructed and
16005 * had their bindings initialized (and before the pre & post linking functions for the directives on
16006 * this element). This is a good place to put initialization code for your controller.
16009 * Require another directive and inject its controller as the fourth argument to the linking function. The
16010 * `require` property can be a string, an array or an object:
16011 * * a **string** containing the name of the directive to pass to the linking function
16012 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
16013 * linking function will be an array of controllers in the same order as the names in the `require` property
16014 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
16015 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
16018 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
16019 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
16020 * have been constructed but before `$onInit` is called.
16021 * See the {@link $compileProvider#component} helper for an example of how this can be used.
16023 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
16024 * raised (unless no link function is specified and the required controllers are not being bound to the directive
16025 * controller, in which case error checking is skipped). The name can be prefixed with:
16027 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
16028 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
16029 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
16030 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
16031 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
16032 * `null` to the `link` fn if not found.
16033 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
16034 * `null` to the `link` fn if not found.
16037 * #### `controllerAs`
16038 * Identifier name for a reference to the controller in the directive's scope.
16039 * This allows the controller to be referenced from the directive template. This is especially
16040 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
16041 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
16042 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
16046 * String of subset of `EACM` which restricts the directive to a specific directive
16047 * declaration style. If omitted, the defaults (elements and attributes) are used.
16049 * * `E` - Element name (default): `<my-directive></my-directive>`
16050 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
16051 * * `C` - Class: `<div class="my-directive: exp;"></div>`
16052 * * `M` - Comment: `<!-- directive: my-directive exp -->`
16055 * #### `templateNamespace`
16056 * String representing the document type used by the markup in the template.
16057 * AngularJS needs this information as those elements need to be created and cloned
16058 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
16060 * * `html` - All root nodes in the template are HTML. Root nodes may also be
16061 * top-level elements such as `<svg>` or `<math>`.
16062 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
16063 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
16065 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
16068 * HTML markup that may:
16069 * * Replace the contents of the directive's element (default).
16070 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
16071 * * Wrap the contents of the directive's element (if `transclude` is true).
16075 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
16076 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
16077 * function api below) and returns a string value.
16080 * #### `templateUrl`
16081 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
16083 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
16084 * for later when the template has been resolved. In the meantime it will continue to compile and link
16085 * sibling and parent elements as though this element had not contained any directives.
16087 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
16088 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
16089 * case when only one deeply nested directive has `templateUrl`.
16091 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
16093 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
16094 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
16095 * a string value representing the url. In either case, the template URL is passed through {@link
16096 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
16099 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
16100 * specify what the template should replace. Defaults to `false`.
16102 * * `true` - the template will replace the directive's element.
16103 * * `false` - the template will replace the contents of the directive's element.
16105 * The replacement process migrates all of the attributes / classes from the old element to the new
16106 * one. See the {@link guide/directive#template-expanding-directive
16107 * Directives Guide} for an example.
16109 * There are very few scenarios where element replacement is required for the application function,
16110 * the main one being reusable custom components that are used within SVG contexts
16111 * (because SVG doesn't work with custom elements in the DOM tree).
16113 * #### `transclude`
16114 * Extract the contents of the element where the directive appears and make it available to the directive.
16115 * The contents are compiled and provided to the directive as a **transclusion function**. See the
16116 * {@link $compile#transclusion Transclusion} section below.
16122 * function compile(tElement, tAttrs, transclude) { ... }
16125 * The compile function deals with transforming the template DOM. Since most directives do not do
16126 * template transformation, it is not used often. The compile function takes the following arguments:
16128 * * `tElement` - template element - The element where the directive has been declared. It is
16129 * safe to do template transformation on the element and child elements only.
16131 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
16132 * between all directive compile functions.
16134 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
16136 * <div class="alert alert-warning">
16137 * **Note:** The template instance and the link instance may be different objects if the template has
16138 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
16139 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
16140 * should be done in a linking function rather than in a compile function.
16143 * <div class="alert alert-warning">
16144 * **Note:** The compile function cannot handle directives that recursively use themselves in their
16145 * own templates or compile functions. Compiling these directives results in an infinite loop and
16146 * stack overflow errors.
16148 * This can be avoided by manually using $compile in the postLink function to imperatively compile
16149 * a directive's template instead of relying on automatic template compilation via `template` or
16150 * `templateUrl` declaration or manual compilation inside the compile function.
16153 * <div class="alert alert-danger">
16154 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
16155 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
16156 * to the link function instead.
16159 * A compile function can have a return value which can be either a function or an object.
16161 * * returning a (post-link) function - is equivalent to registering the linking function via the
16162 * `link` property of the config object when the compile function is empty.
16164 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
16165 * control when a linking function should be called during the linking phase. See info about
16166 * pre-linking and post-linking functions below.
16170 * This property is used only if the `compile` property is not defined.
16173 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
16176 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
16177 * executed after the template has been cloned. This is where most of the directive logic will be
16180 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
16181 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
16183 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
16184 * manipulate the children of the element only in `postLink` function since the children have
16185 * already been linked.
16187 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
16188 * between all directive linking functions.
16190 * * `controller` - the directive's required controller instance(s) - Instances are shared
16191 * among all directives, which allows the directives to use the controllers as a communication
16192 * channel. The exact value depends on the directive's `require` property:
16193 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
16194 * * `string`: the controller instance
16195 * * `array`: array of controller instances
16197 * If a required controller cannot be found, and it is optional, the instance is `null`,
16198 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
16200 * Note that you can also require the directive's own controller - it will be made available like
16201 * any other controller.
16203 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
16204 * This is the same as the `$transclude`
16205 * parameter of directive controllers, see there for details.
16206 * `function([scope], cloneLinkingFn, futureParentElement)`.
16208 * #### Pre-linking function
16210 * Executed before the child elements are linked. Not safe to do DOM transformation since the
16211 * compiler linking function will fail to locate the correct elements for linking.
16213 * #### Post-linking function
16215 * Executed after the child elements are linked.
16217 * Note that child elements that contain `templateUrl` directives will not have been compiled
16218 * and linked since they are waiting for their template to load asynchronously and their own
16219 * compilation and linking has been suspended until that occurs.
16221 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
16222 * for their async templates to be resolved.
16227 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
16228 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
16229 * scope from where they were taken.
16231 * Transclusion is used (often with {@link ngTransclude}) to insert the
16232 * original contents of a directive's element into a specified place in the template of the directive.
16233 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
16234 * content has access to the properties on the scope from which it was taken, even if the directive
16235 * has isolated scope.
16236 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
16238 * This makes it possible for the widget to have private state for its template, while the transcluded
16239 * content has access to its originating scope.
16241 * <div class="alert alert-warning">
16242 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
16243 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
16244 * Testing Transclusion Directives}.
16247 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
16248 * directive's element, the entire element or multiple parts of the element contents:
16250 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
16251 * * `'element'` - transclude the whole of the directive's element including any directives on this
16252 * element that defined at a lower priority than this directive. When used, the `template`
16253 * property is ignored.
16254 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
16256 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
16258 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
16259 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
16260 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
16262 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
16264 * If the element selector is prefixed with a `?` then that slot is optional.
16266 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
16267 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
16269 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
16270 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
16271 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
16272 * injectable into the directive's controller.
16275 * #### Transclusion Functions
16277 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
16278 * function** to the directive's `link` function and `controller`. This transclusion function is a special
16279 * **linking function** that will return the compiled contents linked to a new transclusion scope.
16281 * <div class="alert alert-info">
16282 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
16283 * ngTransclude will deal with it for us.
16286 * If you want to manually control the insertion and removal of the transcluded content in your directive
16287 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
16288 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
16290 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
16291 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
16292 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
16294 * <div class="alert alert-info">
16295 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
16296 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
16299 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
16300 * attach function**:
16303 * var transcludedContent, transclusionScope;
16305 * $transclude(function(clone, scope) {
16306 * element.append(clone);
16307 * transcludedContent = clone;
16308 * transclusionScope = scope;
16312 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
16313 * associated transclusion scope:
16316 * transcludedContent.remove();
16317 * transclusionScope.$destroy();
16320 * <div class="alert alert-info">
16321 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
16322 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
16323 * then you are also responsible for calling `$destroy` on the transclusion scope.
16326 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
16327 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
16328 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
16331 * #### Transclusion Scopes
16333 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
16334 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
16335 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
16338 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
16344 * <div transclusion>
16350 * The `$parent` scope hierarchy will look like this:
16358 * but the scopes will inherit prototypically from different scopes to their `$parent`.
16369 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
16370 * `link()` or `compile()` functions. It has a variety of uses.
16372 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
16373 * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
16374 * to the attributes.
16376 * * *Directive inter-communication:* All directives share the same instance of the attributes
16377 * object which allows the directives to use the attributes object as inter directive
16380 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
16381 * allowing other directives to read the interpolated value.
16383 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
16384 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
16385 * the only way to easily get the actual value because during the linking phase the interpolation
16386 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
16389 * function linkingFn(scope, elm, attrs, ctrl) {
16390 * // get the attribute value
16391 * console.log(attrs.ngModel);
16393 * // change the attribute
16394 * attrs.$set('ngModel', 'new value');
16396 * // observe changes to interpolated attribute
16397 * attrs.$observe('ngModel', function(value) {
16398 * console.log('ngModel has changed value to ' + value);
16405 * <div class="alert alert-warning">
16406 * **Note**: Typically directives are registered with `module.directive`. The example below is
16407 * to illustrate how `$compile` works.
16410 <example module="compileExample">
16411 <file name="index.html">
16413 angular.module('compileExample', [], function($compileProvider) {
16414 // configure new 'compile' directive by passing a directive
16415 // factory function. The factory function injects the '$compile'
16416 $compileProvider.directive('compile', function($compile) {
16417 // directive factory creates a link function
16418 return function(scope, element, attrs) {
16421 // watch the 'compile' expression for changes
16422 return scope.$eval(attrs.compile);
16425 // when the 'compile' expression changes
16426 // assign it into the current DOM
16427 element.html(value);
16429 // compile the new DOM and link it to the current
16431 // NOTE: we only compile .childNodes so that
16432 // we don't get into infinite loop compiling ourselves
16433 $compile(element.contents())(scope);
16439 .controller('GreeterController', ['$scope', function($scope) {
16440 $scope.name = 'Angular';
16441 $scope.html = 'Hello {{name}}';
16444 <div ng-controller="GreeterController">
16445 <input ng-model="name"> <br/>
16446 <textarea ng-model="html"></textarea> <br/>
16447 <div compile="html"></div>
16450 <file name="protractor.js" type="protractor">
16451 it('should auto compile', function() {
16452 var textarea = $('textarea');
16453 var output = $('div[compile]');
16454 // The initial state reads 'Hello Angular'.
16455 expect(output.getText()).toBe('Hello Angular');
16457 textarea.sendKeys('{{name}}!');
16458 expect(output.getText()).toBe('Angular!');
16465 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
16466 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
16468 * <div class="alert alert-danger">
16469 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
16470 * e.g. will not use the right outer scope. Please pass the transclude function as a
16471 * `parentBoundTranscludeFn` to the link function instead.
16474 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
16475 * root element(s), not their children)
16476 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
16477 * (a DOM element/tree) to a scope. Where:
16479 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
16480 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
16481 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
16482 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
16483 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
16485 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
16486 * * `scope` - is the current scope with which the linking function is working with.
16488 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
16489 * keys may be used to control linking behavior:
16491 * * `parentBoundTranscludeFn` - the transclude function made available to
16492 * directives; if given, it will be passed through to the link functions of
16493 * directives found in `element` during compilation.
16494 * * `transcludeControllers` - an object hash with keys that map controller names
16495 * to a hash with the key `instance`, which maps to the controller instance;
16496 * if given, it will make the controllers available to directives on the compileNode:
16500 * instance: parentControllerInstance
16504 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
16505 * the cloned elements; only needed for transcludes that are allowed to contain non html
16506 * elements (e.g. SVG elements). See also the directive.controller property.
16508 * Calling the linking function returns the element of the template. It is either the original
16509 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
16511 * After linking the view is not updated until after a call to $digest which typically is done by
16512 * Angular automatically.
16514 * If you need access to the bound view, there are two ways to do it:
16516 * - If you are not asking the linking function to clone the template, create the DOM element(s)
16517 * before you send them to the compiler and keep this reference around.
16519 * var element = $compile('<p>{{total}}</p>')(scope);
16522 * - if on the other hand, you need the element to be cloned, the view reference from the original
16523 * example would not point to the clone, but rather to the original template that was cloned. In
16524 * this case, you can access the clone via the cloneAttachFn:
16526 * var templateElement = angular.element('<p>{{total}}</p>'),
16529 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
16530 * //attach the clone to DOM document at the right place
16533 * //now we have reference to the cloned DOM via `clonedElement`
16537 * For information on how the compiler works, see the
16538 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
16541 var $compileMinErr = minErr('$compile');
16545 * @name $compileProvider
16549 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
16550 function $CompileProvider($provide, $$sanitizeUriProvider) {
16551 var hasDirectives = {},
16552 Suffix = 'Directive',
16553 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
16554 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
16555 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
16556 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
16558 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
16559 // The assumption is that future DOM event attribute names will begin with
16560 // 'on' and be composed of only English letters.
16561 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
16563 function parseIsolateBindings(scope, directiveName, isController) {
16564 var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
16568 forEach(scope, function(definition, scopeName) {
16569 var match = definition.match(LOCAL_REGEXP);
16572 throw $compileMinErr('iscp',
16573 "Invalid {3} for directive '{0}'." +
16574 " Definition: {... {1}: '{2}' ...}",
16575 directiveName, scopeName, definition,
16576 (isController ? "controller bindings definition" :
16577 "isolate scope definition"));
16580 bindings[scopeName] = {
16582 collection: match[2] === '*',
16583 optional: match[3] === '?',
16584 attrName: match[4] || scopeName
16591 function parseDirectiveBindings(directive, directiveName) {
16593 isolateScope: null,
16594 bindToController: null
16596 if (isObject(directive.scope)) {
16597 if (directive.bindToController === true) {
16598 bindings.bindToController = parseIsolateBindings(directive.scope,
16599 directiveName, true);
16600 bindings.isolateScope = {};
16602 bindings.isolateScope = parseIsolateBindings(directive.scope,
16603 directiveName, false);
16606 if (isObject(directive.bindToController)) {
16607 bindings.bindToController =
16608 parseIsolateBindings(directive.bindToController, directiveName, true);
16610 if (isObject(bindings.bindToController)) {
16611 var controller = directive.controller;
16612 var controllerAs = directive.controllerAs;
16614 // There is no controller, there may or may not be a controllerAs property
16615 throw $compileMinErr('noctrl',
16616 "Cannot bind to controller without directive '{0}'s controller.",
16618 } else if (!identifierForController(controller, controllerAs)) {
16619 // There is a controller, but no identifier or controllerAs property
16620 throw $compileMinErr('noident',
16621 "Cannot bind to controller without identifier for directive '{0}'.",
16628 function assertValidDirectiveName(name) {
16629 var letter = name.charAt(0);
16630 if (!letter || letter !== lowercase(letter)) {
16631 throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
16633 if (name !== name.trim()) {
16634 throw $compileMinErr('baddir',
16635 "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
16642 * @name $compileProvider#directive
16646 * Register a new directive with the compiler.
16648 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
16649 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
16650 * names and the values are the factories.
16651 * @param {Function|Array} directiveFactory An injectable directive factory function. See the
16652 * {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
16653 * @returns {ng.$compileProvider} Self for chaining.
16655 this.directive = function registerDirective(name, directiveFactory) {
16656 assertNotHasOwnProperty(name, 'directive');
16657 if (isString(name)) {
16658 assertValidDirectiveName(name);
16659 assertArg(directiveFactory, 'directiveFactory');
16660 if (!hasDirectives.hasOwnProperty(name)) {
16661 hasDirectives[name] = [];
16662 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
16663 function($injector, $exceptionHandler) {
16664 var directives = [];
16665 forEach(hasDirectives[name], function(directiveFactory, index) {
16667 var directive = $injector.invoke(directiveFactory);
16668 if (isFunction(directive)) {
16669 directive = { compile: valueFn(directive) };
16670 } else if (!directive.compile && directive.link) {
16671 directive.compile = valueFn(directive.link);
16673 directive.priority = directive.priority || 0;
16674 directive.index = index;
16675 directive.name = directive.name || name;
16676 directive.require = directive.require || (directive.controller && directive.name);
16677 directive.restrict = directive.restrict || 'EA';
16678 var bindings = directive.$$bindings =
16679 parseDirectiveBindings(directive, directive.name);
16680 if (isObject(bindings.isolateScope)) {
16681 directive.$$isolateBindings = bindings.isolateScope;
16683 directive.$$moduleName = directiveFactory.$$moduleName;
16684 directives.push(directive);
16686 $exceptionHandler(e);
16692 hasDirectives[name].push(directiveFactory);
16694 forEach(name, reverseParams(registerDirective));
16701 * @name $compileProvider#component
16703 * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
16704 * @param {Object} options Component definition object (a simplified
16705 * {@link ng.$compile#directive-definition-object directive definition object}),
16706 * with the following properties (all optional):
16708 * - `controller` – `{(string|function()=}` – controller constructor function that should be
16709 * associated with newly created scope or the name of a {@link ng.$compile#-controller-
16710 * registered controller} if passed as a string. An empty `noop` function by default.
16711 * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
16712 * If present, the controller will be published to scope under the `controllerAs` name.
16713 * If not present, this will default to be `$ctrl`.
16714 * - `template` – `{string=|function()=}` – html template as a string or a function that
16715 * returns an html template as a string which should be used as the contents of this component.
16716 * Empty string by default.
16718 * If `template` is a function, then it is {@link auto.$injector#invoke injected} with
16719 * the following locals:
16721 * - `$element` - Current element
16722 * - `$attrs` - Current attributes object for the element
16724 * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
16725 * template that should be used as the contents of this component.
16727 * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
16728 * the following locals:
16730 * - `$element` - Current element
16731 * - `$attrs` - Current attributes object for the element
16733 * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
16734 * Component properties are always bound to the component controller and not to the scope.
16735 * See {@link ng.$compile#-bindtocontroller- `bindToController`}.
16736 * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
16737 * Disabled by default.
16738 * - `$...` – `{function()=}` – additional annotations to provide to the directive factory function.
16740 * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
16742 * Register a **component definition** with the compiler. This is a shorthand for registering a special
16743 * type of directive, which represents a self-contained UI component in your application. Such components
16744 * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
16746 * Component definitions are very simple and do not require as much configuration as defining general
16747 * directives. Component definitions usually consist only of a template and a controller backing it.
16749 * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
16750 * `bindToController`. They always have **isolate scope** and are restricted to elements.
16752 * Here are a few examples of how you would usually define components:
16755 * var myMod = angular.module(...);
16756 * myMod.component('myComp', {
16757 * template: '<div>My name is {{$ctrl.name}}</div>',
16758 * controller: function() {
16759 * this.name = 'shahar';
16763 * myMod.component('myComp', {
16764 * template: '<div>My name is {{$ctrl.name}}</div>',
16765 * bindings: {name: '@'}
16768 * myMod.component('myComp', {
16769 * templateUrl: 'views/my-comp.html',
16770 * controller: 'MyCtrl as ctrl',
16771 * bindings: {name: '@'}
16775 * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
16778 * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
16780 this.component = function registerComponent(name, options) {
16781 var controller = options.controller || function() {};
16783 function factory($injector) {
16784 function makeInjectable(fn) {
16785 if (isFunction(fn) || isArray(fn)) {
16786 return function(tElement, tAttrs) {
16787 return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
16794 var template = (!options.template && !options.templateUrl ? '' : options.template);
16796 controller: controller,
16797 controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
16798 template: makeInjectable(template),
16799 templateUrl: makeInjectable(options.templateUrl),
16800 transclude: options.transclude,
16802 bindToController: options.bindings || {},
16804 require: options.require
16808 // Copy any annotation properties (starting with $) over to the factory function
16809 // These could be used by libraries such as the new component router
16810 forEach(options, function(val, key) {
16811 if (key.charAt(0) === '$') {
16812 factory[key] = val;
16816 factory.$inject = ['$injector'];
16818 return this.directive(name, factory);
16824 * @name $compileProvider#aHrefSanitizationWhitelist
16828 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16829 * urls during a[href] sanitization.
16831 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
16833 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16834 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16835 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16836 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16838 * @param {RegExp=} regexp New regexp to whitelist urls with.
16839 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16840 * chaining otherwise.
16842 this.aHrefSanitizationWhitelist = function(regexp) {
16843 if (isDefined(regexp)) {
16844 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
16847 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
16854 * @name $compileProvider#imgSrcSanitizationWhitelist
16858 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16859 * urls during img[src] sanitization.
16861 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16863 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16864 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16865 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16866 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16868 * @param {RegExp=} regexp New regexp to whitelist urls with.
16869 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16870 * chaining otherwise.
16872 this.imgSrcSanitizationWhitelist = function(regexp) {
16873 if (isDefined(regexp)) {
16874 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
16877 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
16883 * @name $compileProvider#debugInfoEnabled
16885 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
16886 * current debugInfoEnabled state
16887 * @returns {*} current value if used as getter or itself (chaining) if used as setter
16892 * Call this method to enable/disable various debug runtime information in the compiler such as adding
16893 * binding information and a reference to the current scope on to DOM elements.
16894 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
16895 * * `ng-binding` CSS class
16896 * * `$binding` data property containing an array of the binding expressions
16898 * You may want to disable this in production for a significant performance boost. See
16899 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
16901 * The default value is true.
16903 var debugInfoEnabled = true;
16904 this.debugInfoEnabled = function(enabled) {
16905 if (isDefined(enabled)) {
16906 debugInfoEnabled = enabled;
16909 return debugInfoEnabled;
16913 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
16914 '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
16915 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
16916 $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
16918 var SIMPLE_ATTR_NAME = /^\w/;
16919 var specialAttrHolder = document.createElement('div');
16920 var Attributes = function(element, attributesToCopy) {
16921 if (attributesToCopy) {
16922 var keys = Object.keys(attributesToCopy);
16925 for (i = 0, l = keys.length; i < l; i++) {
16927 this[key] = attributesToCopy[key];
16933 this.$$element = element;
16936 Attributes.prototype = {
16939 * @name $compile.directive.Attributes#$normalize
16943 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
16944 * `data-`) to its normalized, camelCase form.
16946 * Also there is special case for Moz prefix starting with upper case letter.
16948 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
16950 * @param {string} name Name to normalize
16952 $normalize: directiveNormalize,
16957 * @name $compile.directive.Attributes#$addClass
16961 * Adds the CSS class value specified by the classVal parameter to the element. If animations
16962 * are enabled then an animation will be triggered for the class addition.
16964 * @param {string} classVal The className value that will be added to the element
16966 $addClass: function(classVal) {
16967 if (classVal && classVal.length > 0) {
16968 $animate.addClass(this.$$element, classVal);
16974 * @name $compile.directive.Attributes#$removeClass
16978 * Removes the CSS class value specified by the classVal parameter from the element. If
16979 * animations are enabled then an animation will be triggered for the class removal.
16981 * @param {string} classVal The className value that will be removed from the element
16983 $removeClass: function(classVal) {
16984 if (classVal && classVal.length > 0) {
16985 $animate.removeClass(this.$$element, classVal);
16991 * @name $compile.directive.Attributes#$updateClass
16995 * Adds and removes the appropriate CSS class values to the element based on the difference
16996 * between the new and old CSS class values (specified as newClasses and oldClasses).
16998 * @param {string} newClasses The current CSS className value
16999 * @param {string} oldClasses The former CSS className value
17001 $updateClass: function(newClasses, oldClasses) {
17002 var toAdd = tokenDifference(newClasses, oldClasses);
17003 if (toAdd && toAdd.length) {
17004 $animate.addClass(this.$$element, toAdd);
17007 var toRemove = tokenDifference(oldClasses, newClasses);
17008 if (toRemove && toRemove.length) {
17009 $animate.removeClass(this.$$element, toRemove);
17014 * Set a normalized attribute on the element in a way such that all directives
17015 * can share the attribute. This function properly handles boolean attributes.
17016 * @param {string} key Normalized key. (ie ngAttribute)
17017 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
17018 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
17019 * Defaults to true.
17020 * @param {string=} attrName Optional none normalized name. Defaults to key.
17022 $set: function(key, value, writeAttr, attrName) {
17023 // TODO: decide whether or not to throw an error if "class"
17024 //is set through this function since it may cause $updateClass to
17027 var node = this.$$element[0],
17028 booleanKey = getBooleanAttrName(node, key),
17029 aliasedKey = getAliasedAttrName(key),
17034 this.$$element.prop(key, value);
17035 attrName = booleanKey;
17036 } else if (aliasedKey) {
17037 this[aliasedKey] = value;
17038 observer = aliasedKey;
17043 // translate normalized key to actual key
17045 this.$attr[key] = attrName;
17047 attrName = this.$attr[key];
17049 this.$attr[key] = attrName = snake_case(key, '-');
17053 nodeName = nodeName_(this.$$element);
17055 if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
17056 (nodeName === 'img' && key === 'src')) {
17057 // sanitize a[href] and img[src] values
17058 this[key] = value = $$sanitizeUri(value, key === 'src');
17059 } else if (nodeName === 'img' && key === 'srcset') {
17060 // sanitize img[srcset] values
17063 // first check if there are spaces because it's not the same pattern
17064 var trimmedSrcset = trim(value);
17065 // ( 999x ,| 999w ,| ,|, )
17066 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
17067 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
17069 // split srcset into tuple of uri and descriptor except for the last item
17070 var rawUris = trimmedSrcset.split(pattern);
17073 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
17074 for (var i = 0; i < nbrUrisWith2parts; i++) {
17075 var innerIdx = i * 2;
17076 // sanitize the uri
17077 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
17078 // add the descriptor
17079 result += (" " + trim(rawUris[innerIdx + 1]));
17082 // split the last item into uri and descriptor
17083 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
17085 // sanitize the last uri
17086 result += $$sanitizeUri(trim(lastTuple[0]), true);
17088 // and add the last descriptor if any
17089 if (lastTuple.length === 2) {
17090 result += (" " + trim(lastTuple[1]));
17092 this[key] = value = result;
17095 if (writeAttr !== false) {
17096 if (value === null || isUndefined(value)) {
17097 this.$$element.removeAttr(attrName);
17099 if (SIMPLE_ATTR_NAME.test(attrName)) {
17100 this.$$element.attr(attrName, value);
17102 setSpecialAttr(this.$$element[0], attrName, value);
17108 var $$observers = this.$$observers;
17109 $$observers && forEach($$observers[observer], function(fn) {
17113 $exceptionHandler(e);
17121 * @name $compile.directive.Attributes#$observe
17125 * Observes an interpolated attribute.
17127 * The observer function will be invoked once during the next `$digest` following
17128 * compilation. The observer is then invoked whenever the interpolated value
17131 * @param {string} key Normalized key. (ie ngAttribute) .
17132 * @param {function(interpolatedValue)} fn Function that will be called whenever
17133 the interpolated value of the attribute changes.
17134 * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
17135 * guide} for more info.
17136 * @returns {function()} Returns a deregistration function for this observer.
17138 $observe: function(key, fn) {
17140 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
17141 listeners = ($$observers[key] || ($$observers[key] = []));
17143 listeners.push(fn);
17144 $rootScope.$evalAsync(function() {
17145 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
17146 // no one registered attribute interpolation function, so lets call it manually
17151 return function() {
17152 arrayRemove(listeners, fn);
17157 function setSpecialAttr(element, attrName, value) {
17158 // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
17159 // so we have to jump through some hoops to get such an attribute
17160 // https://github.com/angular/angular.js/pull/13318
17161 specialAttrHolder.innerHTML = "<span " + attrName + ">";
17162 var attributes = specialAttrHolder.firstChild.attributes;
17163 var attribute = attributes[0];
17164 // We have to remove the attribute from its container element before we can add it to the destination element
17165 attributes.removeNamedItem(attribute.name);
17166 attribute.value = value;
17167 element.attributes.setNamedItem(attribute);
17170 function safeAddClass($element, className) {
17172 $element.addClass(className);
17174 // ignore, since it means that we are trying to set class on
17175 // SVG element, where class name is read-only.
17180 var startSymbol = $interpolate.startSymbol(),
17181 endSymbol = $interpolate.endSymbol(),
17182 denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
17184 : function denormalizeTemplate(template) {
17185 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
17187 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
17188 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
17190 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
17191 var bindings = $element.data('$binding') || [];
17193 if (isArray(binding)) {
17194 bindings = bindings.concat(binding);
17196 bindings.push(binding);
17199 $element.data('$binding', bindings);
17202 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
17203 safeAddClass($element, 'ng-binding');
17206 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
17207 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
17208 $element.data(dataName, scope);
17211 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
17212 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
17217 //================================
17219 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
17220 previousCompileContext) {
17221 if (!($compileNodes instanceof jqLite)) {
17222 // jquery always rewraps, whereas we need to preserve the original selector so that we can
17224 $compileNodes = jqLite($compileNodes);
17227 var NOT_EMPTY = /\S+/;
17229 // We can not compile top level text elements since text nodes can be merged and we will
17230 // not be able to attach scope data to them, so we will wrap them in <span>
17231 for (var i = 0, len = $compileNodes.length; i < len; i++) {
17232 var domNode = $compileNodes[i];
17234 if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
17235 jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
17239 var compositeLinkFn =
17240 compileNodes($compileNodes, transcludeFn, $compileNodes,
17241 maxPriority, ignoreDirective, previousCompileContext);
17242 compile.$$addScopeClass($compileNodes);
17243 var namespace = null;
17244 return function publicLinkFn(scope, cloneConnectFn, options) {
17245 assertArg(scope, 'scope');
17247 if (previousCompileContext && previousCompileContext.needsNewScope) {
17248 // A parent directive did a replace and a directive on this element asked
17249 // for transclusion, which caused us to lose a layer of element on which
17250 // we could hold the new transclusion scope, so we will create it manually
17252 scope = scope.$parent.$new();
17255 options = options || {};
17256 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
17257 transcludeControllers = options.transcludeControllers,
17258 futureParentElement = options.futureParentElement;
17260 // When `parentBoundTranscludeFn` is passed, it is a
17261 // `controllersBoundTransclude` function (it was previously passed
17262 // as `transclude` to directive.link) so we must unwrap it to get
17263 // its `boundTranscludeFn`
17264 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
17265 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
17269 namespace = detectNamespaceForChildElements(futureParentElement);
17272 if (namespace !== 'html') {
17273 // When using a directive with replace:true and templateUrl the $compileNodes
17274 // (or a child element inside of them)
17275 // might change, so we need to recreate the namespace adapted compileNodes
17276 // for call to the link function.
17277 // Note: This will already clone the nodes...
17278 $linkNode = jqLite(
17279 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
17281 } else if (cloneConnectFn) {
17282 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
17283 // and sometimes changes the structure of the DOM.
17284 $linkNode = JQLitePrototype.clone.call($compileNodes);
17286 $linkNode = $compileNodes;
17289 if (transcludeControllers) {
17290 for (var controllerName in transcludeControllers) {
17291 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
17295 compile.$$addScopeInfo($linkNode, scope);
17297 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
17298 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
17303 function detectNamespaceForChildElements(parentElement) {
17304 // TODO: Make this detect MathML as well...
17305 var node = parentElement && parentElement[0];
17309 return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
17314 * Compile function matches each node in nodeList against the directives. Once all directives
17315 * for a particular node are collected their compile functions are executed. The compile
17316 * functions return values - the linking functions - are combined into a composite linking
17317 * function, which is the a linking function for the node.
17319 * @param {NodeList} nodeList an array of nodes or NodeList to compile
17320 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
17321 * scope argument is auto-generated to the new child of the transcluded parent scope.
17322 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
17323 * the rootElement must be set the jqLite collection of the compile root. This is
17324 * needed so that the jqLite collection items can be replaced with widgets.
17325 * @param {number=} maxPriority Max directive priority.
17326 * @returns {Function} A composite linking function of all of the matched directives or null.
17328 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
17329 previousCompileContext) {
17331 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
17333 for (var i = 0; i < nodeList.length; i++) {
17334 attrs = new Attributes();
17336 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
17337 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
17340 nodeLinkFn = (directives.length)
17341 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
17342 null, [], [], previousCompileContext)
17345 if (nodeLinkFn && nodeLinkFn.scope) {
17346 compile.$$addScopeClass(attrs.$$element);
17349 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
17350 !(childNodes = nodeList[i].childNodes) ||
17351 !childNodes.length)
17353 : compileNodes(childNodes,
17355 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
17356 && nodeLinkFn.transclude) : transcludeFn);
17358 if (nodeLinkFn || childLinkFn) {
17359 linkFns.push(i, nodeLinkFn, childLinkFn);
17360 linkFnFound = true;
17361 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
17364 //use the previous context only for the first element in the virtual group
17365 previousCompileContext = null;
17368 // return a linking function if we have found anything, null otherwise
17369 return linkFnFound ? compositeLinkFn : null;
17371 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
17372 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
17373 var stableNodeList;
17376 if (nodeLinkFnFound) {
17377 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
17378 // offsets don't get screwed up
17379 var nodeListLength = nodeList.length;
17380 stableNodeList = new Array(nodeListLength);
17382 // create a sparse array by only copying the elements which have a linkFn
17383 for (i = 0; i < linkFns.length; i+=3) {
17385 stableNodeList[idx] = nodeList[idx];
17388 stableNodeList = nodeList;
17391 for (i = 0, ii = linkFns.length; i < ii;) {
17392 node = stableNodeList[linkFns[i++]];
17393 nodeLinkFn = linkFns[i++];
17394 childLinkFn = linkFns[i++];
17397 if (nodeLinkFn.scope) {
17398 childScope = scope.$new();
17399 compile.$$addScopeInfo(jqLite(node), childScope);
17401 childScope = scope;
17404 if (nodeLinkFn.transcludeOnThisElement) {
17405 childBoundTranscludeFn = createBoundTranscludeFn(
17406 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
17408 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
17409 childBoundTranscludeFn = parentBoundTranscludeFn;
17411 } else if (!parentBoundTranscludeFn && transcludeFn) {
17412 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
17415 childBoundTranscludeFn = null;
17418 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
17420 } else if (childLinkFn) {
17421 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
17427 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
17429 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
17431 if (!transcludedScope) {
17432 transcludedScope = scope.$new(false, containingScope);
17433 transcludedScope.$$transcluded = true;
17436 return transcludeFn(transcludedScope, cloneFn, {
17437 parentBoundTranscludeFn: previousBoundTranscludeFn,
17438 transcludeControllers: controllers,
17439 futureParentElement: futureParentElement
17443 // We need to attach the transclusion slots onto the `boundTranscludeFn`
17444 // so that they are available inside the `controllersBoundTransclude` function
17445 var boundSlots = boundTranscludeFn.$$slots = createMap();
17446 for (var slotName in transcludeFn.$$slots) {
17447 if (transcludeFn.$$slots[slotName]) {
17448 boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
17450 boundSlots[slotName] = null;
17454 return boundTranscludeFn;
17458 * Looks for directives on the given node and adds them to the directive collection which is
17461 * @param node Node to search.
17462 * @param directives An array to which the directives are added to. This array is sorted before
17463 * the function returns.
17464 * @param attrs The shared attrs object which is used to populate the normalized attributes.
17465 * @param {number=} maxPriority Max directive priority.
17467 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
17468 var nodeType = node.nodeType,
17469 attrsMap = attrs.$attr,
17473 switch (nodeType) {
17474 case NODE_TYPE_ELEMENT: /* Element */
17475 // use the node name: <directive>
17476 addDirective(directives,
17477 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
17479 // iterate over the attributes
17480 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
17481 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
17482 var attrStartName = false;
17483 var attrEndName = false;
17487 value = trim(attr.value);
17489 // support ngAttr attribute binding
17490 ngAttrName = directiveNormalize(name);
17491 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
17492 name = name.replace(PREFIX_REGEXP, '')
17493 .substr(8).replace(/_(.)/g, function(match, letter) {
17494 return letter.toUpperCase();
17498 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
17499 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
17500 attrStartName = name;
17501 attrEndName = name.substr(0, name.length - 5) + 'end';
17502 name = name.substr(0, name.length - 6);
17505 nName = directiveNormalize(name.toLowerCase());
17506 attrsMap[nName] = name;
17507 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
17508 attrs[nName] = value;
17509 if (getBooleanAttrName(node, nName)) {
17510 attrs[nName] = true; // presence means true
17513 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
17514 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
17518 // use class as directive
17519 className = node.className;
17520 if (isObject(className)) {
17521 // Maybe SVGAnimatedString
17522 className = className.animVal;
17524 if (isString(className) && className !== '') {
17525 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
17526 nName = directiveNormalize(match[2]);
17527 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
17528 attrs[nName] = trim(match[3]);
17530 className = className.substr(match.index + match[0].length);
17534 case NODE_TYPE_TEXT: /* Text Node */
17536 // Workaround for #11781
17537 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
17538 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
17539 node.parentNode.removeChild(node.nextSibling);
17542 addTextInterpolateDirective(directives, node.nodeValue);
17544 case NODE_TYPE_COMMENT: /* Comment */
17546 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
17548 nName = directiveNormalize(match[1]);
17549 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
17550 attrs[nName] = trim(match[2]);
17554 // turns out that under some circumstances IE9 throws errors when one attempts to read
17555 // comment's node value.
17556 // Just ignore it and continue. (Can't seem to reproduce in test case.)
17561 directives.sort(byPriority);
17566 * Given a node with an directive-start it collects all of the siblings until it finds
17573 function groupScan(node, attrStart, attrEnd) {
17576 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
17579 throw $compileMinErr('uterdir',
17580 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
17581 attrStart, attrEnd);
17583 if (node.nodeType == NODE_TYPE_ELEMENT) {
17584 if (node.hasAttribute(attrStart)) depth++;
17585 if (node.hasAttribute(attrEnd)) depth--;
17588 node = node.nextSibling;
17589 } while (depth > 0);
17594 return jqLite(nodes);
17598 * Wrapper for linking function which converts normal linking function into a grouped
17599 * linking function.
17603 * @returns {Function}
17605 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
17606 return function(scope, element, attrs, controllers, transcludeFn) {
17607 element = groupScan(element[0], attrStart, attrEnd);
17608 return linkFn(scope, element, attrs, controllers, transcludeFn);
17613 * A function generator that is used to support both eager and lazy compilation
17614 * linking function.
17616 * @param $compileNodes
17617 * @param transcludeFn
17618 * @param maxPriority
17619 * @param ignoreDirective
17620 * @param previousCompileContext
17621 * @returns {Function}
17623 function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
17625 return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17630 return function() {
17632 compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17634 // Null out all of these references in order to make them eligible for garbage collection
17635 // since this is a potentially long lived closure
17636 $compileNodes = transcludeFn = previousCompileContext = null;
17639 return compiled.apply(this, arguments);
17644 * Once the directives have been collected, their compile functions are executed. This method
17645 * is responsible for inlining directive templates as well as terminating the application
17646 * of the directives if the terminal directive has been reached.
17648 * @param {Array} directives Array of collected directives to execute their compile function.
17649 * this needs to be pre-sorted by priority order.
17650 * @param {Node} compileNode The raw DOM node to apply the compile functions to
17651 * @param {Object} templateAttrs The shared attribute function
17652 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
17653 * scope argument is auto-generated to the new
17654 * child of the transcluded parent scope.
17655 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
17656 * argument has the root jqLite array so that we can replace nodes
17658 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
17659 * compiling the transclusion.
17660 * @param {Array.<Function>} preLinkFns
17661 * @param {Array.<Function>} postLinkFns
17662 * @param {Object} previousCompileContext Context used for previous compilation of the current
17664 * @returns {Function} linkFn
17666 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
17667 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
17668 previousCompileContext) {
17669 previousCompileContext = previousCompileContext || {};
17671 var terminalPriority = -Number.MAX_VALUE,
17672 newScopeDirective = previousCompileContext.newScopeDirective,
17673 controllerDirectives = previousCompileContext.controllerDirectives,
17674 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
17675 templateDirective = previousCompileContext.templateDirective,
17676 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
17677 hasTranscludeDirective = false,
17678 hasTemplate = false,
17679 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
17680 $compileNode = templateAttrs.$$element = jqLite(compileNode),
17684 replaceDirective = originalReplaceDirective,
17685 childTranscludeFn = transcludeFn,
17687 didScanForMultipleTransclusion = false,
17688 mightHaveMultipleTransclusionError = false,
17691 // executes all directives on the current element
17692 for (var i = 0, ii = directives.length; i < ii; i++) {
17693 directive = directives[i];
17694 var attrStart = directive.$$start;
17695 var attrEnd = directive.$$end;
17697 // collect multiblock sections
17699 $compileNode = groupScan(compileNode, attrStart, attrEnd);
17701 $template = undefined;
17703 if (terminalPriority > directive.priority) {
17704 break; // prevent further processing of directives
17707 if (directiveValue = directive.scope) {
17709 // skip the check for directives with async templates, we'll check the derived sync
17710 // directive when the template arrives
17711 if (!directive.templateUrl) {
17712 if (isObject(directiveValue)) {
17713 // This directive is trying to add an isolated scope.
17714 // Check that there is no scope of any kind already
17715 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
17716 directive, $compileNode);
17717 newIsolateScopeDirective = directive;
17719 // This directive is trying to add a child scope.
17720 // Check that there is no isolated scope already
17721 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
17726 newScopeDirective = newScopeDirective || directive;
17729 directiveName = directive.name;
17731 // If we encounter a condition that can result in transclusion on the directive,
17732 // then scan ahead in the remaining directives for others that may cause a multiple
17733 // transclusion error to be thrown during the compilation process. If a matching directive
17734 // is found, then we know that when we encounter a transcluded directive, we need to eagerly
17735 // compile the `transclude` function rather than doing it lazily in order to throw
17736 // exceptions at the correct time
17737 if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
17738 || (directive.transclude && !directive.$$tlb))) {
17739 var candidateDirective;
17741 for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
17742 if ((candidateDirective.transclude && !candidateDirective.$$tlb)
17743 || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
17744 mightHaveMultipleTransclusionError = true;
17749 didScanForMultipleTransclusion = true;
17752 if (!directive.templateUrl && directive.controller) {
17753 directiveValue = directive.controller;
17754 controllerDirectives = controllerDirectives || createMap();
17755 assertNoDuplicate("'" + directiveName + "' controller",
17756 controllerDirectives[directiveName], directive, $compileNode);
17757 controllerDirectives[directiveName] = directive;
17760 if (directiveValue = directive.transclude) {
17761 hasTranscludeDirective = true;
17763 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
17764 // This option should only be used by directives that know how to safely handle element transclusion,
17765 // where the transcluded nodes are added or replaced after linking.
17766 if (!directive.$$tlb) {
17767 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
17768 nonTlbTranscludeDirective = directive;
17771 if (directiveValue == 'element') {
17772 hasElementTranscludeDirective = true;
17773 terminalPriority = directive.priority;
17774 $template = $compileNode;
17775 $compileNode = templateAttrs.$$element =
17776 jqLite(document.createComment(' ' + directiveName + ': ' +
17777 templateAttrs[directiveName] + ' '));
17778 compileNode = $compileNode[0];
17779 replaceWith(jqCollection, sliceArgs($template), compileNode);
17781 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
17782 replaceDirective && replaceDirective.name, {
17784 // - controllerDirectives - otherwise we'll create duplicates controllers
17785 // - newIsolateScopeDirective or templateDirective - combining templates with
17786 // element transclusion doesn't make sense.
17788 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
17789 // on the same element more than once.
17790 nonTlbTranscludeDirective: nonTlbTranscludeDirective
17794 var slots = createMap();
17796 $template = jqLite(jqLiteClone(compileNode)).contents();
17798 if (isObject(directiveValue)) {
17800 // We have transclusion slots,
17801 // collect them up, compile them and store their transclusion functions
17804 var slotMap = createMap();
17805 var filledSlots = createMap();
17807 // Parse the element selectors
17808 forEach(directiveValue, function(elementSelector, slotName) {
17809 // If an element selector starts with a ? then it is optional
17810 var optional = (elementSelector.charAt(0) === '?');
17811 elementSelector = optional ? elementSelector.substring(1) : elementSelector;
17813 slotMap[elementSelector] = slotName;
17815 // We explicitly assign `null` since this implies that a slot was defined but not filled.
17816 // Later when calling boundTransclusion functions with a slot name we only error if the
17817 // slot is `undefined`
17818 slots[slotName] = null;
17820 // filledSlots contains `true` for all slots that are either optional or have been
17821 // filled. This is used to check that we have not missed any required slots
17822 filledSlots[slotName] = optional;
17825 // Add the matching elements into their slot
17826 forEach($compileNode.contents(), function(node) {
17827 var slotName = slotMap[directiveNormalize(nodeName_(node))];
17829 filledSlots[slotName] = true;
17830 slots[slotName] = slots[slotName] || [];
17831 slots[slotName].push(node);
17833 $template.push(node);
17837 // Check for required slots that were not filled
17838 forEach(filledSlots, function(filled, slotName) {
17840 throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
17844 for (var slotName in slots) {
17845 if (slots[slotName]) {
17846 // Only define a transclusion function if the slot was filled
17847 slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
17852 $compileNode.empty(); // clear contents
17853 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
17854 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
17855 childTranscludeFn.$$slots = slots;
17859 if (directive.template) {
17860 hasTemplate = true;
17861 assertNoDuplicate('template', templateDirective, directive, $compileNode);
17862 templateDirective = directive;
17864 directiveValue = (isFunction(directive.template))
17865 ? directive.template($compileNode, templateAttrs)
17866 : directive.template;
17868 directiveValue = denormalizeTemplate(directiveValue);
17870 if (directive.replace) {
17871 replaceDirective = directive;
17872 if (jqLiteIsTextNode(directiveValue)) {
17875 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
17877 compileNode = $template[0];
17879 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
17880 throw $compileMinErr('tplrt',
17881 "Template for directive '{0}' must have exactly one root element. {1}",
17882 directiveName, '');
17885 replaceWith(jqCollection, $compileNode, compileNode);
17887 var newTemplateAttrs = {$attr: {}};
17889 // combine directives from the original node and from the template:
17890 // - take the array of directives for this element
17891 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
17892 // - collect directives from the template and sort them by priority
17893 // - combine directives as: processed + template + unprocessed
17894 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
17895 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
17897 if (newIsolateScopeDirective || newScopeDirective) {
17898 // The original directive caused the current element to be replaced but this element
17899 // also needs to have a new scope, so we need to tell the template directives
17900 // that they would need to get their scope from further up, if they require transclusion
17901 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
17903 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
17904 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
17906 ii = directives.length;
17908 $compileNode.html(directiveValue);
17912 if (directive.templateUrl) {
17913 hasTemplate = true;
17914 assertNoDuplicate('template', templateDirective, directive, $compileNode);
17915 templateDirective = directive;
17917 if (directive.replace) {
17918 replaceDirective = directive;
17921 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
17922 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
17923 controllerDirectives: controllerDirectives,
17924 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
17925 newIsolateScopeDirective: newIsolateScopeDirective,
17926 templateDirective: templateDirective,
17927 nonTlbTranscludeDirective: nonTlbTranscludeDirective
17929 ii = directives.length;
17930 } else if (directive.compile) {
17932 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
17933 if (isFunction(linkFn)) {
17934 addLinkFns(null, linkFn, attrStart, attrEnd);
17935 } else if (linkFn) {
17936 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
17939 $exceptionHandler(e, startingTag($compileNode));
17943 if (directive.terminal) {
17944 nodeLinkFn.terminal = true;
17945 terminalPriority = Math.max(terminalPriority, directive.priority);
17950 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
17951 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
17952 nodeLinkFn.templateOnThisElement = hasTemplate;
17953 nodeLinkFn.transclude = childTranscludeFn;
17955 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
17957 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
17960 ////////////////////
17962 function addLinkFns(pre, post, attrStart, attrEnd) {
17964 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
17965 pre.require = directive.require;
17966 pre.directiveName = directiveName;
17967 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
17968 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
17970 preLinkFns.push(pre);
17973 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
17974 post.require = directive.require;
17975 post.directiveName = directiveName;
17976 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
17977 post = cloneAndAnnotateFn(post, {isolateScope: true});
17979 postLinkFns.push(post);
17984 function getControllers(directiveName, require, $element, elementControllers) {
17987 if (isString(require)) {
17988 var match = require.match(REQUIRE_PREFIX_REGEXP);
17989 var name = require.substring(match[0].length);
17990 var inheritType = match[1] || match[3];
17991 var optional = match[2] === '?';
17993 //If only parents then start at the parent element
17994 if (inheritType === '^^') {
17995 $element = $element.parent();
17996 //Otherwise attempt getting the controller from elementControllers in case
17997 //the element is transcluded (and has no data) and to avoid .data if possible
17999 value = elementControllers && elementControllers[name];
18000 value = value && value.instance;
18004 var dataName = '$' + name + 'Controller';
18005 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
18008 if (!value && !optional) {
18009 throw $compileMinErr('ctreq',
18010 "Controller '{0}', required by directive '{1}', can't be found!",
18011 name, directiveName);
18013 } else if (isArray(require)) {
18015 for (var i = 0, ii = require.length; i < ii; i++) {
18016 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
18018 } else if (isObject(require)) {
18020 forEach(require, function(controller, property) {
18021 value[property] = getControllers(directiveName, controller, $element, elementControllers);
18025 return value || null;
18028 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
18029 var elementControllers = createMap();
18030 for (var controllerKey in controllerDirectives) {
18031 var directive = controllerDirectives[controllerKey];
18033 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
18034 $element: $element,
18036 $transclude: transcludeFn
18039 var controller = directive.controller;
18040 if (controller == '@') {
18041 controller = attrs[directive.name];
18044 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
18046 // For directives with element transclusion the element is a comment,
18047 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
18048 // clean up (http://bugs.jquery.com/ticket/8335).
18049 // Instead, we save the controllers for the element in a local hash and attach to .data
18050 // later, once we have the actual element.
18051 elementControllers[directive.name] = controllerInstance;
18052 if (!hasElementTranscludeDirective) {
18053 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
18056 return elementControllers;
18059 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
18060 var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
18061 attrs, removeScopeBindingWatches, removeControllerBindingWatches;
18063 if (compileNode === linkNode) {
18064 attrs = templateAttrs;
18065 $element = templateAttrs.$$element;
18067 $element = jqLite(linkNode);
18068 attrs = new Attributes($element, templateAttrs);
18071 controllerScope = scope;
18072 if (newIsolateScopeDirective) {
18073 isolateScope = scope.$new(true);
18074 } else if (newScopeDirective) {
18075 controllerScope = scope.$parent;
18078 if (boundTranscludeFn) {
18079 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
18080 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
18081 transcludeFn = controllersBoundTransclude;
18082 transcludeFn.$$boundTransclude = boundTranscludeFn;
18083 // expose the slots on the `$transclude` function
18084 transcludeFn.isSlotFilled = function(slotName) {
18085 return !!boundTranscludeFn.$$slots[slotName];
18089 if (controllerDirectives) {
18090 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
18093 if (newIsolateScopeDirective) {
18094 // Initialize isolate scope bindings for new isolate scope directive.
18095 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
18096 templateDirective === newIsolateScopeDirective.$$originalDirective)));
18097 compile.$$addScopeClass($element, true);
18098 isolateScope.$$isolateBindings =
18099 newIsolateScopeDirective.$$isolateBindings;
18100 removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
18101 isolateScope.$$isolateBindings,
18102 newIsolateScopeDirective);
18103 if (removeScopeBindingWatches) {
18104 isolateScope.$on('$destroy', removeScopeBindingWatches);
18108 // Initialize bindToController bindings
18109 for (var name in elementControllers) {
18110 var controllerDirective = controllerDirectives[name];
18111 var controller = elementControllers[name];
18112 var bindings = controllerDirective.$$bindings.bindToController;
18114 if (controller.identifier && bindings) {
18115 removeControllerBindingWatches =
18116 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
18119 var controllerResult = controller();
18120 if (controllerResult !== controller.instance) {
18121 // If the controller constructor has a return value, overwrite the instance
18122 // from setupControllers
18123 controller.instance = controllerResult;
18124 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
18125 removeControllerBindingWatches && removeControllerBindingWatches();
18126 removeControllerBindingWatches =
18127 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
18131 // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
18132 forEach(controllerDirectives, function(controllerDirective, name) {
18133 var require = controllerDirective.require;
18134 if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
18135 extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
18139 // Trigger the `$onInit` method on all controllers that have one
18140 forEach(elementControllers, function(controller) {
18141 if (isFunction(controller.instance.$onInit)) {
18142 controller.instance.$onInit();
18147 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
18148 linkFn = preLinkFns[i];
18149 invokeLinkFn(linkFn,
18150 linkFn.isolateScope ? isolateScope : scope,
18153 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
18159 // We only pass the isolate scope, if the isolate directive has a template,
18160 // otherwise the child elements do not belong to the isolate directive.
18161 var scopeToChild = scope;
18162 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
18163 scopeToChild = isolateScope;
18165 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
18168 for (i = postLinkFns.length - 1; i >= 0; i--) {
18169 linkFn = postLinkFns[i];
18170 invokeLinkFn(linkFn,
18171 linkFn.isolateScope ? isolateScope : scope,
18174 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
18179 // This is the function that is injected as `$transclude`.
18180 // Note: all arguments are optional!
18181 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
18182 var transcludeControllers;
18183 // No scope passed in:
18184 if (!isScope(scope)) {
18185 slotName = futureParentElement;
18186 futureParentElement = cloneAttachFn;
18187 cloneAttachFn = scope;
18191 if (hasElementTranscludeDirective) {
18192 transcludeControllers = elementControllers;
18194 if (!futureParentElement) {
18195 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
18198 // slotTranscludeFn can be one of three things:
18199 // * a transclude function - a filled slot
18200 // * `null` - an optional slot that was not filled
18201 // * `undefined` - a slot that was not declared (i.e. invalid)
18202 var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
18203 if (slotTranscludeFn) {
18204 return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
18205 } else if (isUndefined(slotTranscludeFn)) {
18206 throw $compileMinErr('noslot',
18207 'No parent directive that requires a transclusion with slot name "{0}". ' +
18209 slotName, startingTag($element));
18212 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
18218 // Depending upon the context in which a directive finds itself it might need to have a new isolated
18219 // or child scope created. For instance:
18220 // * if the directive has been pulled into a template because another directive with a higher priority
18221 // asked for element transclusion
18222 // * if the directive itself asks for transclusion but it is at the root of a template and the original
18223 // element was replaced. See https://github.com/angular/angular.js/issues/12936
18224 function markDirectiveScope(directives, isolateScope, newScope) {
18225 for (var j = 0, jj = directives.length; j < jj; j++) {
18226 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
18231 * looks up the directive and decorates it with exception handling and proper parameters. We
18232 * call this the boundDirective.
18234 * @param {string} name name of the directive to look up.
18235 * @param {string} location The directive must be found in specific format.
18236 * String containing any of theses characters:
18238 * * `E`: element name
18242 * @returns {boolean} true if directive was added.
18244 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
18246 if (name === ignoreDirective) return null;
18248 if (hasDirectives.hasOwnProperty(name)) {
18249 for (var directive, directives = $injector.get(name + Suffix),
18250 i = 0, ii = directives.length; i < ii; i++) {
18252 directive = directives[i];
18253 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
18254 directive.restrict.indexOf(location) != -1) {
18255 if (startAttrName) {
18256 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
18258 tDirectives.push(directive);
18261 } catch (e) { $exceptionHandler(e); }
18269 * looks up the directive and returns true if it is a multi-element directive,
18270 * and therefore requires DOM nodes between -start and -end markers to be grouped
18273 * @param {string} name name of the directive to look up.
18274 * @returns true if directive was registered as multi-element.
18276 function directiveIsMultiElement(name) {
18277 if (hasDirectives.hasOwnProperty(name)) {
18278 for (var directive, directives = $injector.get(name + Suffix),
18279 i = 0, ii = directives.length; i < ii; i++) {
18280 directive = directives[i];
18281 if (directive.multiElement) {
18290 * When the element is replaced with HTML template then the new attributes
18291 * on the template need to be merged with the existing attributes in the DOM.
18292 * The desired effect is to have both of the attributes present.
18294 * @param {object} dst destination attributes (original DOM)
18295 * @param {object} src source attributes (from the directive template)
18297 function mergeTemplateAttributes(dst, src) {
18298 var srcAttr = src.$attr,
18299 dstAttr = dst.$attr,
18300 $element = dst.$$element;
18302 // reapply the old attributes to the new element
18303 forEach(dst, function(value, key) {
18304 if (key.charAt(0) != '$') {
18305 if (src[key] && src[key] !== value) {
18306 value += (key === 'style' ? ';' : ' ') + src[key];
18308 dst.$set(key, value, true, srcAttr[key]);
18312 // copy the new attributes on the old attrs object
18313 forEach(src, function(value, key) {
18314 if (key == 'class') {
18315 safeAddClass($element, value);
18316 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
18317 } else if (key == 'style') {
18318 $element.attr('style', $element.attr('style') + ';' + value);
18319 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
18320 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
18321 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
18322 // have an attribute like "has-own-property" or "data-has-own-property", etc.
18323 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
18325 dstAttr[key] = srcAttr[key];
18331 function compileTemplateUrl(directives, $compileNode, tAttrs,
18332 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
18333 var linkQueue = [],
18334 afterTemplateNodeLinkFn,
18335 afterTemplateChildLinkFn,
18336 beforeTemplateCompileNode = $compileNode[0],
18337 origAsyncDirective = directives.shift(),
18338 derivedSyncDirective = inherit(origAsyncDirective, {
18339 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
18341 templateUrl = (isFunction(origAsyncDirective.templateUrl))
18342 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
18343 : origAsyncDirective.templateUrl,
18344 templateNamespace = origAsyncDirective.templateNamespace;
18346 $compileNode.empty();
18348 $templateRequest(templateUrl)
18349 .then(function(content) {
18350 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
18352 content = denormalizeTemplate(content);
18354 if (origAsyncDirective.replace) {
18355 if (jqLiteIsTextNode(content)) {
18358 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
18360 compileNode = $template[0];
18362 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
18363 throw $compileMinErr('tplrt',
18364 "Template for directive '{0}' must have exactly one root element. {1}",
18365 origAsyncDirective.name, templateUrl);
18368 tempTemplateAttrs = {$attr: {}};
18369 replaceWith($rootElement, $compileNode, compileNode);
18370 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
18372 if (isObject(origAsyncDirective.scope)) {
18373 // the original directive that caused the template to be loaded async required
18374 // an isolate scope
18375 markDirectiveScope(templateDirectives, true);
18377 directives = templateDirectives.concat(directives);
18378 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
18380 compileNode = beforeTemplateCompileNode;
18381 $compileNode.html(content);
18384 directives.unshift(derivedSyncDirective);
18386 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
18387 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
18388 previousCompileContext);
18389 forEach($rootElement, function(node, i) {
18390 if (node == compileNode) {
18391 $rootElement[i] = $compileNode[0];
18394 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
18396 while (linkQueue.length) {
18397 var scope = linkQueue.shift(),
18398 beforeTemplateLinkNode = linkQueue.shift(),
18399 linkRootElement = linkQueue.shift(),
18400 boundTranscludeFn = linkQueue.shift(),
18401 linkNode = $compileNode[0];
18403 if (scope.$$destroyed) continue;
18405 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
18406 var oldClasses = beforeTemplateLinkNode.className;
18408 if (!(previousCompileContext.hasElementTranscludeDirective &&
18409 origAsyncDirective.replace)) {
18410 // it was cloned therefore we have to clone as well.
18411 linkNode = jqLiteClone(compileNode);
18413 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
18415 // Copy in CSS classes from original node
18416 safeAddClass(jqLite(linkNode), oldClasses);
18418 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
18419 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
18421 childBoundTranscludeFn = boundTranscludeFn;
18423 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
18424 childBoundTranscludeFn);
18429 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
18430 var childBoundTranscludeFn = boundTranscludeFn;
18431 if (scope.$$destroyed) return;
18433 linkQueue.push(scope,
18436 childBoundTranscludeFn);
18438 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
18439 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
18441 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
18448 * Sorting function for bound directives.
18450 function byPriority(a, b) {
18451 var diff = b.priority - a.priority;
18452 if (diff !== 0) return diff;
18453 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
18454 return a.index - b.index;
18457 function assertNoDuplicate(what, previousDirective, directive, element) {
18459 function wrapModuleNameIfDefined(moduleName) {
18460 return moduleName ?
18461 (' (module: ' + moduleName + ')') :
18465 if (previousDirective) {
18466 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
18467 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
18468 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
18473 function addTextInterpolateDirective(directives, text) {
18474 var interpolateFn = $interpolate(text, true);
18475 if (interpolateFn) {
18478 compile: function textInterpolateCompileFn(templateNode) {
18479 var templateNodeParent = templateNode.parent(),
18480 hasCompileParent = !!templateNodeParent.length;
18482 // When transcluding a template that has bindings in the root
18483 // we don't have a parent and thus need to add the class during linking fn.
18484 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
18486 return function textInterpolateLinkFn(scope, node) {
18487 var parent = node.parent();
18488 if (!hasCompileParent) compile.$$addBindingClass(parent);
18489 compile.$$addBindingInfo(parent, interpolateFn.expressions);
18490 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
18491 node[0].nodeValue = value;
18500 function wrapTemplate(type, template) {
18501 type = lowercase(type || 'html');
18505 var wrapper = document.createElement('div');
18506 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
18507 return wrapper.childNodes[0].childNodes;
18514 function getTrustedContext(node, attrNormalizedName) {
18515 if (attrNormalizedName == "srcdoc") {
18518 var tag = nodeName_(node);
18519 // maction[xlink:href] can source SVG. It's not limited to <maction>.
18520 if (attrNormalizedName == "xlinkHref" ||
18521 (tag == "form" && attrNormalizedName == "action") ||
18522 (tag != "img" && (attrNormalizedName == "src" ||
18523 attrNormalizedName == "ngSrc"))) {
18524 return $sce.RESOURCE_URL;
18529 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
18530 var trustedContext = getTrustedContext(node, name);
18531 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
18533 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
18535 // no interpolation found -> ignore
18536 if (!interpolateFn) return;
18539 if (name === "multiple" && nodeName_(node) === "select") {
18540 throw $compileMinErr("selmulti",
18541 "Binding to the 'multiple' attribute is not supported. Element: {0}",
18542 startingTag(node));
18547 compile: function() {
18549 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
18550 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
18552 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
18553 throw $compileMinErr('nodomevents',
18554 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
18555 "ng- versions (such as ng-click instead of onclick) instead.");
18558 // If the attribute has changed since last $interpolate()ed
18559 var newValue = attr[name];
18560 if (newValue !== value) {
18561 // we need to interpolate again since the attribute value has been updated
18562 // (e.g. by another directive's compile function)
18563 // ensure unset/empty values make interpolateFn falsy
18564 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
18568 // if attribute was updated so that there is no interpolation going on we don't want to
18569 // register any observers
18570 if (!interpolateFn) return;
18572 // initialize attr object so that it's ready in case we need the value for isolate
18573 // scope initialization, otherwise the value would not be available from isolate
18574 // directive's linking fn during linking phase
18575 attr[name] = interpolateFn(scope);
18577 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
18578 (attr.$$observers && attr.$$observers[name].$$scope || scope).
18579 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
18580 //special case for class attribute addition + removal
18581 //so that class changes can tap into the animation
18582 //hooks provided by the $animate service. Be sure to
18583 //skip animations when the first digest occurs (when
18584 //both the new and the old values are the same) since
18585 //the CSS classes are the non-interpolated values
18586 if (name === 'class' && newValue != oldValue) {
18587 attr.$updateClass(newValue, oldValue);
18589 attr.$set(name, newValue);
18600 * This is a special jqLite.replaceWith, which can replace items which
18601 * have no parents, provided that the containing jqLite collection is provided.
18603 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
18604 * in the root of the tree.
18605 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
18606 * the shell, but replace its DOM node reference.
18607 * @param {Node} newNode The new DOM node.
18609 function replaceWith($rootElement, elementsToRemove, newNode) {
18610 var firstElementToRemove = elementsToRemove[0],
18611 removeCount = elementsToRemove.length,
18612 parent = firstElementToRemove.parentNode,
18615 if ($rootElement) {
18616 for (i = 0, ii = $rootElement.length; i < ii; i++) {
18617 if ($rootElement[i] == firstElementToRemove) {
18618 $rootElement[i++] = newNode;
18619 for (var j = i, j2 = j + removeCount - 1,
18620 jj = $rootElement.length;
18621 j < jj; j++, j2++) {
18623 $rootElement[j] = $rootElement[j2];
18625 delete $rootElement[j];
18628 $rootElement.length -= removeCount - 1;
18630 // If the replaced element is also the jQuery .context then replace it
18631 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
18632 // http://api.jquery.com/context/
18633 if ($rootElement.context === firstElementToRemove) {
18634 $rootElement.context = newNode;
18642 parent.replaceChild(newNode, firstElementToRemove);
18645 // Append all the `elementsToRemove` to a fragment. This will...
18646 // - remove them from the DOM
18647 // - allow them to still be traversed with .nextSibling
18648 // - allow a single fragment.qSA to fetch all elements being removed
18649 var fragment = document.createDocumentFragment();
18650 for (i = 0; i < removeCount; i++) {
18651 fragment.appendChild(elementsToRemove[i]);
18654 if (jqLite.hasData(firstElementToRemove)) {
18655 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
18656 // data here because there's no public interface in jQuery to do that and copying over
18657 // event listeners (which is the main use of private data) wouldn't work anyway.
18658 jqLite.data(newNode, jqLite.data(firstElementToRemove));
18660 // Remove $destroy event listeners from `firstElementToRemove`
18661 jqLite(firstElementToRemove).off('$destroy');
18664 // Cleanup any data/listeners on the elements and children.
18665 // This includes invoking the $destroy event on any elements with listeners.
18666 jqLite.cleanData(fragment.querySelectorAll('*'));
18668 // Update the jqLite collection to only contain the `newNode`
18669 for (i = 1; i < removeCount; i++) {
18670 delete elementsToRemove[i];
18672 elementsToRemove[0] = newNode;
18673 elementsToRemove.length = 1;
18677 function cloneAndAnnotateFn(fn, annotation) {
18678 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
18682 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
18684 linkFn(scope, $element, attrs, controllers, transcludeFn);
18686 $exceptionHandler(e, startingTag($element));
18691 // Set up $watches for isolate scope and controller bindings. This process
18692 // only occurs for isolate scopes and new scopes with controllerAs.
18693 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
18694 var removeWatchCollection = [];
18695 forEach(bindings, function(definition, scopeName) {
18696 var attrName = definition.attrName,
18697 optional = definition.optional,
18698 mode = definition.mode, // @, =, or &
18700 parentGet, parentSet, compare, removeWatch;
18705 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
18706 destination[scopeName] = attrs[attrName] = void 0;
18708 attrs.$observe(attrName, function(value) {
18709 if (isString(value)) {
18710 destination[scopeName] = value;
18713 attrs.$$observers[attrName].$$scope = scope;
18714 lastValue = attrs[attrName];
18715 if (isString(lastValue)) {
18716 // If the attribute has been provided then we trigger an interpolation to ensure
18717 // the value is there for use in the link fn
18718 destination[scopeName] = $interpolate(lastValue)(scope);
18719 } else if (isBoolean(lastValue)) {
18720 // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
18721 // the value to boolean rather than a string, so we special case this situation
18722 destination[scopeName] = lastValue;
18727 if (!hasOwnProperty.call(attrs, attrName)) {
18728 if (optional) break;
18729 attrs[attrName] = void 0;
18731 if (optional && !attrs[attrName]) break;
18733 parentGet = $parse(attrs[attrName]);
18734 if (parentGet.literal) {
18737 compare = function(a, b) { return a === b || (a !== a && b !== b); };
18739 parentSet = parentGet.assign || function() {
18740 // reset the change, or we will throw this exception on every $digest
18741 lastValue = destination[scopeName] = parentGet(scope);
18742 throw $compileMinErr('nonassign',
18743 "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
18744 attrs[attrName], attrName, directive.name);
18746 lastValue = destination[scopeName] = parentGet(scope);
18747 var parentValueWatch = function parentValueWatch(parentValue) {
18748 if (!compare(parentValue, destination[scopeName])) {
18749 // we are out of sync and need to copy
18750 if (!compare(parentValue, lastValue)) {
18751 // parent changed and it has precedence
18752 destination[scopeName] = parentValue;
18754 // if the parent can be assigned then do so
18755 parentSet(scope, parentValue = destination[scopeName]);
18758 return lastValue = parentValue;
18760 parentValueWatch.$stateful = true;
18761 if (definition.collection) {
18762 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
18764 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
18766 removeWatchCollection.push(removeWatch);
18770 if (!hasOwnProperty.call(attrs, attrName)) {
18771 if (optional) break;
18772 attrs[attrName] = void 0;
18774 if (optional && !attrs[attrName]) break;
18776 parentGet = $parse(attrs[attrName]);
18778 destination[scopeName] = parentGet(scope);
18780 removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
18781 destination[scopeName] = newParentValue;
18782 }, parentGet.literal);
18784 removeWatchCollection.push(removeWatch);
18788 // Don't assign Object.prototype method to scope
18789 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
18791 // Don't assign noop to destination if expression is not valid
18792 if (parentGet === noop && optional) break;
18794 destination[scopeName] = function(locals) {
18795 return parentGet(scope, locals);
18801 return removeWatchCollection.length && function removeWatches() {
18802 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
18803 removeWatchCollection[i]();
18810 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
18812 * Converts all accepted directives format into proper directive name.
18813 * @param name Name to normalize
18815 function directiveNormalize(name) {
18816 return camelCase(name.replace(PREFIX_REGEXP, ''));
18821 * @name $compile.directive.Attributes
18824 * A shared object between directive compile / linking functions which contains normalized DOM
18825 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
18826 * needed since all of these are treated as equivalent in Angular:
18829 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
18835 * @name $compile.directive.Attributes#$attr
18838 * A map of DOM element attribute names to the normalized name. This is
18839 * needed to do reverse lookup from normalized name back to actual name.
18845 * @name $compile.directive.Attributes#$set
18849 * Set DOM element attribute value.
18852 * @param {string} name Normalized element attribute name of the property to modify. The name is
18853 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
18854 * property to the original name.
18855 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
18861 * Closure compiler type information
18864 function nodesetLinkingFn(
18865 /* angular.Scope */ scope,
18866 /* NodeList */ nodeList,
18867 /* Element */ rootElement,
18868 /* function(Function) */ boundTranscludeFn
18871 function directiveLinkingFn(
18872 /* nodesetLinkingFn */ nodesetLinkingFn,
18873 /* angular.Scope */ scope,
18875 /* Element */ rootElement,
18876 /* function(Function) */ boundTranscludeFn
18879 function tokenDifference(str1, str2) {
18881 tokens1 = str1.split(/\s+/),
18882 tokens2 = str2.split(/\s+/);
18885 for (var i = 0; i < tokens1.length; i++) {
18886 var token = tokens1[i];
18887 for (var j = 0; j < tokens2.length; j++) {
18888 if (token == tokens2[j]) continue outer;
18890 values += (values.length > 0 ? ' ' : '') + token;
18895 function removeComments(jqNodes) {
18896 jqNodes = jqLite(jqNodes);
18897 var i = jqNodes.length;
18904 var node = jqNodes[i];
18905 if (node.nodeType === NODE_TYPE_COMMENT) {
18906 splice.call(jqNodes, i, 1);
18912 var $controllerMinErr = minErr('$controller');
18915 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
18916 function identifierForController(controller, ident) {
18917 if (ident && isString(ident)) return ident;
18918 if (isString(controller)) {
18919 var match = CNTRL_REG.exec(controller);
18920 if (match) return match[3];
18927 * @name $controllerProvider
18929 * The {@link ng.$controller $controller service} is used by Angular to create new
18932 * This provider allows controller registration via the
18933 * {@link ng.$controllerProvider#register register} method.
18935 function $ControllerProvider() {
18936 var controllers = {},
18941 * @name $controllerProvider#register
18942 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
18943 * the names and the values are the constructors.
18944 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
18945 * annotations in the array notation).
18947 this.register = function(name, constructor) {
18948 assertNotHasOwnProperty(name, 'controller');
18949 if (isObject(name)) {
18950 extend(controllers, name);
18952 controllers[name] = constructor;
18958 * @name $controllerProvider#allowGlobals
18959 * @description If called, allows `$controller` to find controller constructors on `window`
18961 this.allowGlobals = function() {
18966 this.$get = ['$injector', '$window', function($injector, $window) {
18970 * @name $controller
18971 * @requires $injector
18973 * @param {Function|string} constructor If called with a function then it's considered to be the
18974 * controller constructor function. Otherwise it's considered to be a string which is used
18975 * to retrieve the controller constructor using the following steps:
18977 * * check if a controller with given name is registered via `$controllerProvider`
18978 * * check if evaluating the string on the current scope returns a constructor
18979 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
18980 * `window` object (not recommended)
18982 * The string can use the `controller as property` syntax, where the controller instance is published
18983 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
18984 * to work correctly.
18986 * @param {Object} locals Injection locals for Controller.
18987 * @return {Object} Instance of given controller.
18990 * `$controller` service is responsible for instantiating controllers.
18992 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
18993 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
18995 return function(expression, locals, later, ident) {
18997 // param `later` --- indicates that the controller's constructor is invoked at a later time.
18998 // If true, $controller will allocate the object with the correct
18999 // prototype chain, but will not invoke the controller until a returned
19000 // callback is invoked.
19001 // param `ident` --- An optional label which overrides the label parsed from the controller
19002 // expression, if any.
19003 var instance, match, constructor, identifier;
19004 later = later === true;
19005 if (ident && isString(ident)) {
19006 identifier = ident;
19009 if (isString(expression)) {
19010 match = expression.match(CNTRL_REG);
19012 throw $controllerMinErr('ctrlfmt',
19013 "Badly formed controller string '{0}'. " +
19014 "Must match `__name__ as __id__` or `__name__`.", expression);
19016 constructor = match[1],
19017 identifier = identifier || match[3];
19018 expression = controllers.hasOwnProperty(constructor)
19019 ? controllers[constructor]
19020 : getter(locals.$scope, constructor, true) ||
19021 (globals ? getter($window, constructor, true) : undefined);
19023 assertArgFn(expression, constructor, true);
19027 // Instantiate controller later:
19028 // This machinery is used to create an instance of the object before calling the
19029 // controller's constructor itself.
19031 // This allows properties to be added to the controller before the constructor is
19032 // invoked. Primarily, this is used for isolate scope bindings in $compile.
19034 // This feature is not intended for use by applications, and is thus not documented
19036 // Object creation: http://jsperf.com/create-constructor/2
19037 var controllerPrototype = (isArray(expression) ?
19038 expression[expression.length - 1] : expression).prototype;
19039 instance = Object.create(controllerPrototype || null);
19042 addIdentifier(locals, identifier, instance, constructor || expression.name);
19046 return instantiate = extend(function() {
19047 var result = $injector.invoke(expression, instance, locals, constructor);
19048 if (result !== instance && (isObject(result) || isFunction(result))) {
19051 // If result changed, re-assign controllerAs value to scope.
19052 addIdentifier(locals, identifier, instance, constructor || expression.name);
19057 instance: instance,
19058 identifier: identifier
19062 instance = $injector.instantiate(expression, locals, constructor);
19065 addIdentifier(locals, identifier, instance, constructor || expression.name);
19071 function addIdentifier(locals, identifier, instance, name) {
19072 if (!(locals && isObject(locals.$scope))) {
19073 throw minErr('$controller')('noscp',
19074 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
19078 locals.$scope[identifier] = instance;
19086 * @requires $window
19089 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
19092 <example module="documentExample">
19093 <file name="index.html">
19094 <div ng-controller="ExampleController">
19095 <p>$document title: <b ng-bind="title"></b></p>
19096 <p>window.document title: <b ng-bind="windowTitle"></b></p>
19099 <file name="script.js">
19100 angular.module('documentExample', [])
19101 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
19102 $scope.title = $document[0].title;
19103 $scope.windowTitle = angular.element(window.document)[0].title;
19108 function $DocumentProvider() {
19109 this.$get = ['$window', function(window) {
19110 return jqLite(window.document);
19116 * @name $exceptionHandler
19117 * @requires ng.$log
19120 * Any uncaught exception in angular expressions is delegated to this service.
19121 * The default implementation simply delegates to `$log.error` which logs it into
19122 * the browser console.
19124 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
19125 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
19130 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
19131 * return function(exception, cause) {
19132 * exception.message += ' (caused by "' + cause + '")';
19138 * This example will override the normal action of `$exceptionHandler`, to make angular
19139 * exceptions fail hard when they happen, instead of just logging to the console.
19142 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
19143 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
19144 * (unless executed during a digest).
19146 * If you wish, you can manually delegate exceptions, e.g.
19147 * `try { ... } catch(e) { $exceptionHandler(e); }`
19149 * @param {Error} exception Exception associated with the error.
19150 * @param {string=} cause optional information about the context in which
19151 * the error was thrown.
19154 function $ExceptionHandlerProvider() {
19155 this.$get = ['$log', function($log) {
19156 return function(exception, cause) {
19157 $log.error.apply($log, arguments);
19162 var $$ForceReflowProvider = function() {
19163 this.$get = ['$document', function($document) {
19164 return function(domNode) {
19165 //the line below will force the browser to perform a repaint so
19166 //that all the animated elements within the animation frame will
19167 //be properly updated and drawn on screen. This is required to
19168 //ensure that the preparation animation is properly flushed so that
19169 //the active state picks up from there. DO NOT REMOVE THIS LINE.
19170 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
19171 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
19172 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
19174 if (!domNode.nodeType && domNode instanceof jqLite) {
19175 domNode = domNode[0];
19178 domNode = $document[0].body;
19180 return domNode.offsetWidth + 1;
19185 var APPLICATION_JSON = 'application/json';
19186 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
19187 var JSON_START = /^\[|^\{(?!\{)/;
19192 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
19193 var $httpMinErr = minErr('$http');
19194 var $httpMinErrLegacyFn = function(method) {
19195 return function() {
19196 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
19200 function serializeValue(v) {
19202 return isDate(v) ? v.toISOString() : toJson(v);
19208 function $HttpParamSerializerProvider() {
19211 * @name $httpParamSerializer
19214 * Default {@link $http `$http`} params serializer that converts objects to strings
19215 * according to the following rules:
19217 * * `{'foo': 'bar'}` results in `foo=bar`
19218 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
19219 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
19220 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
19222 * Note that serializer will sort the request parameters alphabetically.
19225 this.$get = function() {
19226 return function ngParamSerializer(params) {
19227 if (!params) return '';
19229 forEachSorted(params, function(value, key) {
19230 if (value === null || isUndefined(value)) return;
19231 if (isArray(value)) {
19232 forEach(value, function(v, k) {
19233 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
19236 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
19240 return parts.join('&');
19245 function $HttpParamSerializerJQLikeProvider() {
19248 * @name $httpParamSerializerJQLike
19251 * Alternative {@link $http `$http`} params serializer that follows
19252 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
19253 * The serializer will also sort the params alphabetically.
19255 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
19261 * params: myParams,
19262 * paramSerializer: '$httpParamSerializerJQLike'
19266 * It is also possible to set it as the default `paramSerializer` in the
19267 * {@link $httpProvider#defaults `$httpProvider`}.
19269 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
19270 * form data for submission:
19273 * .controller(function($http, $httpParamSerializerJQLike) {
19279 * data: $httpParamSerializerJQLike(myData),
19281 * 'Content-Type': 'application/x-www-form-urlencoded'
19289 this.$get = function() {
19290 return function jQueryLikeParamSerializer(params) {
19291 if (!params) return '';
19293 serialize(params, '', true);
19294 return parts.join('&');
19296 function serialize(toSerialize, prefix, topLevel) {
19297 if (toSerialize === null || isUndefined(toSerialize)) return;
19298 if (isArray(toSerialize)) {
19299 forEach(toSerialize, function(value, index) {
19300 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
19302 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
19303 forEachSorted(toSerialize, function(value, key) {
19304 serialize(value, prefix +
19305 (topLevel ? '' : '[') +
19307 (topLevel ? '' : ']'));
19310 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
19317 function defaultHttpResponseTransform(data, headers) {
19318 if (isString(data)) {
19319 // Strip json vulnerability protection prefix and trim whitespace
19320 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
19323 var contentType = headers('Content-Type');
19324 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
19325 data = fromJson(tempData);
19333 function isJsonLike(str) {
19334 var jsonStart = str.match(JSON_START);
19335 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
19339 * Parse headers into key value object
19341 * @param {string} headers Raw headers as a string
19342 * @returns {Object} Parsed headers as key value object
19344 function parseHeaders(headers) {
19345 var parsed = createMap(), i;
19347 function fillInParsed(key, val) {
19349 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
19353 if (isString(headers)) {
19354 forEach(headers.split('\n'), function(line) {
19355 i = line.indexOf(':');
19356 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
19358 } else if (isObject(headers)) {
19359 forEach(headers, function(headerVal, headerKey) {
19360 fillInParsed(lowercase(headerKey), trim(headerVal));
19369 * Returns a function that provides access to parsed headers.
19371 * Headers are lazy parsed when first requested.
19372 * @see parseHeaders
19374 * @param {(string|Object)} headers Headers to provide access to.
19375 * @returns {function(string=)} Returns a getter function which if called with:
19377 * - if called with single an argument returns a single header value or null
19378 * - if called with no arguments returns an object containing all headers.
19380 function headersGetter(headers) {
19383 return function(name) {
19384 if (!headersObj) headersObj = parseHeaders(headers);
19387 var value = headersObj[lowercase(name)];
19388 if (value === void 0) {
19400 * Chain all given functions
19402 * This function is used for both request and response transforming
19404 * @param {*} data Data to transform.
19405 * @param {function(string=)} headers HTTP headers getter fn.
19406 * @param {number} status HTTP status code of the response.
19407 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
19408 * @returns {*} Transformed data.
19410 function transformData(data, headers, status, fns) {
19411 if (isFunction(fns)) {
19412 return fns(data, headers, status);
19415 forEach(fns, function(fn) {
19416 data = fn(data, headers, status);
19423 function isSuccess(status) {
19424 return 200 <= status && status < 300;
19430 * @name $httpProvider
19432 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
19434 function $HttpProvider() {
19437 * @name $httpProvider#defaults
19440 * Object containing default values for all {@link ng.$http $http} requests.
19442 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
19443 * that will provide the cache for all requests who set their `cache` property to `true`.
19444 * If you set the `defaults.cache = false` then only requests that specify their own custom
19445 * cache object will be cached. See {@link $http#caching $http Caching} for more information.
19447 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
19448 * Defaults value is `'XSRF-TOKEN'`.
19450 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
19451 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
19453 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
19454 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
19455 * setting default headers.
19456 * - **`defaults.headers.common`**
19457 * - **`defaults.headers.post`**
19458 * - **`defaults.headers.put`**
19459 * - **`defaults.headers.patch`**
19462 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
19463 * used to the prepare string representation of request parameters (specified as an object).
19464 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
19465 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
19468 var defaults = this.defaults = {
19469 // transform incoming response data
19470 transformResponse: [defaultHttpResponseTransform],
19472 // transform outgoing request data
19473 transformRequest: [function(d) {
19474 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
19480 'Accept': 'application/json, text/plain, */*'
19482 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
19483 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
19484 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
19487 xsrfCookieName: 'XSRF-TOKEN',
19488 xsrfHeaderName: 'X-XSRF-TOKEN',
19490 paramSerializer: '$httpParamSerializer'
19493 var useApplyAsync = false;
19496 * @name $httpProvider#useApplyAsync
19499 * Configure $http service to combine processing of multiple http responses received at around
19500 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
19501 * significant performance improvement for bigger applications that make many HTTP requests
19502 * concurrently (common during application bootstrap).
19504 * Defaults to false. If no value is specified, returns the current configured value.
19506 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
19507 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
19508 * to load and share the same digest cycle.
19510 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
19511 * otherwise, returns the current configured value.
19513 this.useApplyAsync = function(value) {
19514 if (isDefined(value)) {
19515 useApplyAsync = !!value;
19518 return useApplyAsync;
19521 var useLegacyPromise = true;
19524 * @name $httpProvider#useLegacyPromiseExtensions
19527 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
19528 * This should be used to make sure that applications work without these methods.
19530 * Defaults to true. If no value is specified, returns the current configured value.
19532 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
19534 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
19535 * otherwise, returns the current configured value.
19537 this.useLegacyPromiseExtensions = function(value) {
19538 if (isDefined(value)) {
19539 useLegacyPromise = !!value;
19542 return useLegacyPromise;
19547 * @name $httpProvider#interceptors
19550 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
19551 * pre-processing of request or postprocessing of responses.
19553 * These service factories are ordered by request, i.e. they are applied in the same order as the
19554 * array, on request, but reverse order, on response.
19556 * {@link ng.$http#interceptors Interceptors detailed info}
19558 var interceptorFactories = this.interceptors = [];
19560 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
19561 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
19563 var defaultCache = $cacheFactory('$http');
19566 * Make sure that default param serializer is exposed as a function
19568 defaults.paramSerializer = isString(defaults.paramSerializer) ?
19569 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
19572 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
19573 * The reversal is needed so that we can build up the interception chain around the
19576 var reversedInterceptors = [];
19578 forEach(interceptorFactories, function(interceptorFactory) {
19579 reversedInterceptors.unshift(isString(interceptorFactory)
19580 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
19587 * @requires ng.$httpBackend
19588 * @requires $cacheFactory
19589 * @requires $rootScope
19591 * @requires $injector
19594 * The `$http` service is a core Angular service that facilitates communication with the remote
19595 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
19596 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
19598 * For unit testing applications that use `$http` service, see
19599 * {@link ngMock.$httpBackend $httpBackend mock}.
19601 * For a higher level of abstraction, please check out the {@link ngResource.$resource
19602 * $resource} service.
19604 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
19605 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
19606 * it is important to familiarize yourself with these APIs and the guarantees they provide.
19610 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
19611 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
19614 * // Simple GET request example:
19618 * }).then(function successCallback(response) {
19619 * // this callback will be called asynchronously
19620 * // when the response is available
19621 * }, function errorCallback(response) {
19622 * // called asynchronously if an error occurs
19623 * // or server returns response with an error status.
19627 * The response object has these properties:
19629 * - **data** – `{string|Object}` – The response body transformed with the transform
19631 * - **status** – `{number}` – HTTP status code of the response.
19632 * - **headers** – `{function([headerName])}` – Header getter function.
19633 * - **config** – `{Object}` – The configuration object that was used to generate the request.
19634 * - **statusText** – `{string}` – HTTP status text of the response.
19636 * A response status code between 200 and 299 is considered a success status and
19637 * will result in the success callback being called. Note that if the response is a redirect,
19638 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
19639 * called for such responses.
19642 * ## Shortcut methods
19644 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
19645 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
19649 * $http.get('/someUrl', config).then(successCallback, errorCallback);
19650 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
19653 * Complete list of shortcut methods:
19655 * - {@link ng.$http#get $http.get}
19656 * - {@link ng.$http#head $http.head}
19657 * - {@link ng.$http#post $http.post}
19658 * - {@link ng.$http#put $http.put}
19659 * - {@link ng.$http#delete $http.delete}
19660 * - {@link ng.$http#jsonp $http.jsonp}
19661 * - {@link ng.$http#patch $http.patch}
19664 * ## Writing Unit Tests that use $http
19665 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
19666 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
19667 * request using trained responses.
19670 * $httpBackend.expectGET(...);
19672 * $httpBackend.flush();
19675 * ## Deprecation Notice
19676 * <div class="alert alert-danger">
19677 * The `$http` legacy promise methods `success` and `error` have been deprecated.
19678 * Use the standard `then` method instead.
19679 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
19680 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
19683 * ## Setting HTTP Headers
19685 * The $http service will automatically add certain HTTP headers to all requests. These defaults
19686 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
19687 * object, which currently contains this default configuration:
19689 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
19690 * - `Accept: application/json, text/plain, * / *`
19691 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
19692 * - `Content-Type: application/json`
19693 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
19694 * - `Content-Type: application/json`
19696 * To add or overwrite these defaults, simply add or remove a property from these configuration
19697 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
19698 * with the lowercased HTTP method name as the key, e.g.
19699 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
19701 * The defaults can also be set at runtime via the `$http.defaults` object in the same
19702 * fashion. For example:
19705 * module.run(function($http) {
19706 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
19710 * In addition, you can supply a `headers` property in the config object passed when
19711 * calling `$http(config)`, which overrides the defaults without changing them globally.
19713 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
19714 * Use the `headers` property, setting the desired header to `undefined`. For example:
19719 * url: 'http://example.com',
19721 * 'Content-Type': undefined
19723 * data: { test: 'test' }
19726 * $http(req).then(function(){...}, function(){...});
19729 * ## Transforming Requests and Responses
19731 * Both requests and responses can be transformed using transformation functions: `transformRequest`
19732 * and `transformResponse`. These properties can be a single function that returns
19733 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
19734 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
19736 * ### Default Transformations
19738 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
19739 * `defaults.transformResponse` properties. If a request does not provide its own transformations
19740 * then these will be applied.
19742 * You can augment or replace the default transformations by modifying these properties by adding to or
19743 * replacing the array.
19745 * Angular provides the following default transformations:
19747 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
19749 * - If the `data` property of the request configuration object contains an object, serialize it
19750 * into JSON format.
19752 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
19754 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
19755 * - If JSON response is detected, deserialize it using a JSON parser.
19758 * ### Overriding the Default Transformations Per Request
19760 * If you wish override the request/response transformations only for a single request then provide
19761 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
19764 * Note that if you provide these properties on the config object the default transformations will be
19765 * overwritten. If you wish to augment the default transformations then you must include them in your
19766 * local transformation array.
19768 * The following code demonstrates adding a new response transformation to be run after the default response
19769 * transformations have been run.
19772 * function appendTransform(defaults, transform) {
19774 * // We can't guarantee that the default transformation is an array
19775 * defaults = angular.isArray(defaults) ? defaults : [defaults];
19777 * // Append the new transformation to the defaults
19778 * return defaults.concat(transform);
19784 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
19785 * return doTransform(value);
19793 * To enable caching, set the request configuration `cache` property to `true` (to use default
19794 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
19795 * When the cache is enabled, `$http` stores the response from the server in the specified
19796 * cache. The next time the same request is made, the response is served from the cache without
19797 * sending a request to the server.
19799 * Note that even if the response is served from cache, delivery of the data is asynchronous in
19800 * the same way that real requests are.
19802 * If there are multiple GET requests for the same URL that should be cached using the same
19803 * cache, but the cache is not populated yet, only one request to the server will be made and
19804 * the remaining requests will be fulfilled using the response from the first request.
19806 * You can change the default cache to a new object (built with
19807 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
19808 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
19809 * their `cache` property to `true` will now use this cache object.
19811 * If you set the default cache to `false` then only requests that specify their own custom
19812 * cache object will be cached.
19816 * Before you start creating interceptors, be sure to understand the
19817 * {@link ng.$q $q and deferred/promise APIs}.
19819 * For purposes of global error handling, authentication, or any kind of synchronous or
19820 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
19821 * able to intercept requests before they are handed to the server and
19822 * responses before they are handed over to the application code that
19823 * initiated these requests. The interceptors leverage the {@link ng.$q
19824 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
19826 * The interceptors are service factories that are registered with the `$httpProvider` by
19827 * adding them to the `$httpProvider.interceptors` array. The factory is called and
19828 * injected with dependencies (if specified) and returns the interceptor.
19830 * There are two kinds of interceptors (and two kinds of rejection interceptors):
19832 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
19833 * modify the `config` object or create a new one. The function needs to return the `config`
19834 * object directly, or a promise containing the `config` or a new `config` object.
19835 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
19836 * resolved with a rejection.
19837 * * `response`: interceptors get called with http `response` object. The function is free to
19838 * modify the `response` object or create a new one. The function needs to return the `response`
19839 * object directly, or as a promise containing the `response` or a new `response` object.
19840 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
19841 * resolved with a rejection.
19845 * // register the interceptor as a service
19846 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
19848 * // optional method
19849 * 'request': function(config) {
19850 * // do something on success
19854 * // optional method
19855 * 'requestError': function(rejection) {
19856 * // do something on error
19857 * if (canRecover(rejection)) {
19858 * return responseOrNewPromise
19860 * return $q.reject(rejection);
19865 * // optional method
19866 * 'response': function(response) {
19867 * // do something on success
19871 * // optional method
19872 * 'responseError': function(rejection) {
19873 * // do something on error
19874 * if (canRecover(rejection)) {
19875 * return responseOrNewPromise
19877 * return $q.reject(rejection);
19882 * $httpProvider.interceptors.push('myHttpInterceptor');
19885 * // alternatively, register the interceptor via an anonymous factory
19886 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
19888 * 'request': function(config) {
19892 * 'response': function(response) {
19899 * ## Security Considerations
19901 * When designing web applications, consider security threats from:
19903 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
19904 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
19906 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
19907 * pre-configured with strategies that address these issues, but for this to work backend server
19908 * cooperation is required.
19910 * ### JSON Vulnerability Protection
19912 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
19913 * allows third party website to turn your JSON resource URL into
19914 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
19915 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
19916 * Angular will automatically strip the prefix before processing it as JSON.
19918 * For example if your server needs to return:
19923 * which is vulnerable to attack, your server can return:
19929 * Angular will strip the prefix, before processing the JSON.
19932 * ### Cross Site Request Forgery (XSRF) Protection
19934 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
19935 * which the attacker can trick an authenticated user into unknowingly executing actions on your
19936 * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
19937 * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
19938 * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
19939 * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
19940 * The header will not be set for cross-domain requests.
19942 * To take advantage of this, your server needs to set a token in a JavaScript readable session
19943 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
19944 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
19945 * that only JavaScript running on your domain could have sent the request. The token must be
19946 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
19947 * making up its own tokens). We recommend that the token is a digest of your site's
19948 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
19949 * for added security.
19951 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
19952 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
19953 * or the per-request config object.
19955 * In order to prevent collisions in environments where multiple Angular apps share the
19956 * same domain or subdomain, we recommend that each application uses unique cookie name.
19958 * @param {object} config Object describing the request to be made and how it should be
19959 * processed. The object has following properties:
19961 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
19962 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
19963 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
19964 * with the `paramSerializer` and appended as GET parameters.
19965 * - **data** – `{string|Object}` – Data to be sent as the request message data.
19966 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
19967 * HTTP headers to send to the server. If the return value of a function is null, the
19968 * header will not be sent. Functions accept a config object as an argument.
19969 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
19970 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
19971 * - **transformRequest** –
19972 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
19973 * transform function or an array of such functions. The transform function takes the http
19974 * request body and headers and returns its transformed (typically serialized) version.
19975 * See {@link ng.$http#overriding-the-default-transformations-per-request
19976 * Overriding the Default Transformations}
19977 * - **transformResponse** –
19978 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
19979 * transform function or an array of such functions. The transform function takes the http
19980 * response body, headers and status and returns its transformed (typically deserialized) version.
19981 * See {@link ng.$http#overriding-the-default-transformations-per-request
19982 * Overriding the Default TransformationjqLiks}
19983 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
19984 * prepare the string representation of request parameters (specified as an object).
19985 * If specified as string, it is interpreted as function registered with the
19986 * {@link $injector $injector}, which means you can create your own serializer
19987 * by registering it as a {@link auto.$provide#service service}.
19988 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
19989 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
19990 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
19991 * GET request, otherwise if a cache instance built with
19992 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
19994 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
19995 * that should abort the request when resolved.
19996 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
19997 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
19998 * for more information.
19999 * - **responseType** - `{string}` - see
20000 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
20002 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
20003 * when the request succeeds or fails.
20006 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
20007 * requests. This is primarily meant to be used for debugging purposes.
20011 <example module="httpExample">
20012 <file name="index.html">
20013 <div ng-controller="FetchController">
20014 <select ng-model="method" aria-label="Request method">
20015 <option>GET</option>
20016 <option>JSONP</option>
20018 <input type="text" ng-model="url" size="80" aria-label="URL" />
20019 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
20020 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
20021 <button id="samplejsonpbtn"
20022 ng-click="updateModel('JSONP',
20023 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
20026 <button id="invalidjsonpbtn"
20027 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
20030 <pre>http status code: {{status}}</pre>
20031 <pre>http response data: {{data}}</pre>
20034 <file name="script.js">
20035 angular.module('httpExample', [])
20036 .controller('FetchController', ['$scope', '$http', '$templateCache',
20037 function($scope, $http, $templateCache) {
20038 $scope.method = 'GET';
20039 $scope.url = 'http-hello.html';
20041 $scope.fetch = function() {
20042 $scope.code = null;
20043 $scope.response = null;
20045 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
20046 then(function(response) {
20047 $scope.status = response.status;
20048 $scope.data = response.data;
20049 }, function(response) {
20050 $scope.data = response.data || "Request failed";
20051 $scope.status = response.status;
20055 $scope.updateModel = function(method, url) {
20056 $scope.method = method;
20061 <file name="http-hello.html">
20064 <file name="protractor.js" type="protractor">
20065 var status = element(by.binding('status'));
20066 var data = element(by.binding('data'));
20067 var fetchBtn = element(by.id('fetchbtn'));
20068 var sampleGetBtn = element(by.id('samplegetbtn'));
20069 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
20070 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
20072 it('should make an xhr GET request', function() {
20073 sampleGetBtn.click();
20075 expect(status.getText()).toMatch('200');
20076 expect(data.getText()).toMatch(/Hello, \$http!/);
20079 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
20080 // it('should make a JSONP request to angularjs.org', function() {
20081 // sampleJsonpBtn.click();
20082 // fetchBtn.click();
20083 // expect(status.getText()).toMatch('200');
20084 // expect(data.getText()).toMatch(/Super Hero!/);
20087 it('should make JSONP request to invalid URL and invoke the error handler',
20089 invalidJsonpBtn.click();
20091 expect(status.getText()).toMatch('0');
20092 expect(data.getText()).toMatch('Request failed');
20097 function $http(requestConfig) {
20099 if (!isObject(requestConfig)) {
20100 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
20103 if (!isString(requestConfig.url)) {
20104 throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
20107 var config = extend({
20109 transformRequest: defaults.transformRequest,
20110 transformResponse: defaults.transformResponse,
20111 paramSerializer: defaults.paramSerializer
20114 config.headers = mergeHeaders(requestConfig);
20115 config.method = uppercase(config.method);
20116 config.paramSerializer = isString(config.paramSerializer) ?
20117 $injector.get(config.paramSerializer) : config.paramSerializer;
20119 var serverRequest = function(config) {
20120 var headers = config.headers;
20121 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
20123 // strip content-type if data is undefined
20124 if (isUndefined(reqData)) {
20125 forEach(headers, function(value, header) {
20126 if (lowercase(header) === 'content-type') {
20127 delete headers[header];
20132 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
20133 config.withCredentials = defaults.withCredentials;
20137 return sendReq(config, reqData).then(transformResponse, transformResponse);
20140 var chain = [serverRequest, undefined];
20141 var promise = $q.when(config);
20143 // apply interceptors
20144 forEach(reversedInterceptors, function(interceptor) {
20145 if (interceptor.request || interceptor.requestError) {
20146 chain.unshift(interceptor.request, interceptor.requestError);
20148 if (interceptor.response || interceptor.responseError) {
20149 chain.push(interceptor.response, interceptor.responseError);
20153 while (chain.length) {
20154 var thenFn = chain.shift();
20155 var rejectFn = chain.shift();
20157 promise = promise.then(thenFn, rejectFn);
20160 if (useLegacyPromise) {
20161 promise.success = function(fn) {
20162 assertArgFn(fn, 'fn');
20164 promise.then(function(response) {
20165 fn(response.data, response.status, response.headers, config);
20170 promise.error = function(fn) {
20171 assertArgFn(fn, 'fn');
20173 promise.then(null, function(response) {
20174 fn(response.data, response.status, response.headers, config);
20179 promise.success = $httpMinErrLegacyFn('success');
20180 promise.error = $httpMinErrLegacyFn('error');
20185 function transformResponse(response) {
20186 // make a copy since the response must be cacheable
20187 var resp = extend({}, response);
20188 resp.data = transformData(response.data, response.headers, response.status,
20189 config.transformResponse);
20190 return (isSuccess(response.status))
20195 function executeHeaderFns(headers, config) {
20196 var headerContent, processedHeaders = {};
20198 forEach(headers, function(headerFn, header) {
20199 if (isFunction(headerFn)) {
20200 headerContent = headerFn(config);
20201 if (headerContent != null) {
20202 processedHeaders[header] = headerContent;
20205 processedHeaders[header] = headerFn;
20209 return processedHeaders;
20212 function mergeHeaders(config) {
20213 var defHeaders = defaults.headers,
20214 reqHeaders = extend({}, config.headers),
20215 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
20217 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
20219 // using for-in instead of forEach to avoid unnecessary iteration after header has been found
20220 defaultHeadersIteration:
20221 for (defHeaderName in defHeaders) {
20222 lowercaseDefHeaderName = lowercase(defHeaderName);
20224 for (reqHeaderName in reqHeaders) {
20225 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
20226 continue defaultHeadersIteration;
20230 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
20233 // execute if header value is a function for merged headers
20234 return executeHeaderFns(reqHeaders, shallowCopy(config));
20238 $http.pendingRequests = [];
20245 * Shortcut method to perform `GET` request.
20247 * @param {string} url Relative or absolute URL specifying the destination of the request
20248 * @param {Object=} config Optional configuration object
20249 * @returns {HttpPromise} Future object
20254 * @name $http#delete
20257 * Shortcut method to perform `DELETE` request.
20259 * @param {string} url Relative or absolute URL specifying the destination of the request
20260 * @param {Object=} config Optional configuration object
20261 * @returns {HttpPromise} Future object
20269 * Shortcut method to perform `HEAD` request.
20271 * @param {string} url Relative or absolute URL specifying the destination of the request
20272 * @param {Object=} config Optional configuration object
20273 * @returns {HttpPromise} Future object
20278 * @name $http#jsonp
20281 * Shortcut method to perform `JSONP` request.
20283 * @param {string} url Relative or absolute URL specifying the destination of the request.
20284 * The name of the callback should be the string `JSON_CALLBACK`.
20285 * @param {Object=} config Optional configuration object
20286 * @returns {HttpPromise} Future object
20288 createShortMethods('get', 'delete', 'head', 'jsonp');
20295 * Shortcut method to perform `POST` request.
20297 * @param {string} url Relative or absolute URL specifying the destination of the request
20298 * @param {*} data Request content
20299 * @param {Object=} config Optional configuration object
20300 * @returns {HttpPromise} Future object
20308 * Shortcut method to perform `PUT` request.
20310 * @param {string} url Relative or absolute URL specifying the destination of the request
20311 * @param {*} data Request content
20312 * @param {Object=} config Optional configuration object
20313 * @returns {HttpPromise} Future object
20318 * @name $http#patch
20321 * Shortcut method to perform `PATCH` request.
20323 * @param {string} url Relative or absolute URL specifying the destination of the request
20324 * @param {*} data Request content
20325 * @param {Object=} config Optional configuration object
20326 * @returns {HttpPromise} Future object
20328 createShortMethodsWithData('post', 'put', 'patch');
20332 * @name $http#defaults
20335 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
20336 * default headers, withCredentials as well as request and response transformations.
20338 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
20340 $http.defaults = defaults;
20346 function createShortMethods(names) {
20347 forEach(arguments, function(name) {
20348 $http[name] = function(url, config) {
20349 return $http(extend({}, config || {}, {
20358 function createShortMethodsWithData(name) {
20359 forEach(arguments, function(name) {
20360 $http[name] = function(url, data, config) {
20361 return $http(extend({}, config || {}, {
20372 * Makes the request.
20374 * !!! ACCESSES CLOSURE VARS:
20375 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
20377 function sendReq(config, reqData) {
20378 var deferred = $q.defer(),
20379 promise = deferred.promise,
20382 reqHeaders = config.headers,
20383 url = buildUrl(config.url, config.paramSerializer(config.params));
20385 $http.pendingRequests.push(config);
20386 promise.then(removePendingReq, removePendingReq);
20389 if ((config.cache || defaults.cache) && config.cache !== false &&
20390 (config.method === 'GET' || config.method === 'JSONP')) {
20391 cache = isObject(config.cache) ? config.cache
20392 : isObject(defaults.cache) ? defaults.cache
20397 cachedResp = cache.get(url);
20398 if (isDefined(cachedResp)) {
20399 if (isPromiseLike(cachedResp)) {
20400 // cached request has already been sent, but there is no response yet
20401 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
20403 // serving from cache
20404 if (isArray(cachedResp)) {
20405 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
20407 resolvePromise(cachedResp, 200, {}, 'OK');
20411 // put the promise for the non-transformed response into cache as a placeholder
20412 cache.put(url, promise);
20417 // if we won't have the response in cache, set the xsrf headers and
20418 // send the request to the backend
20419 if (isUndefined(cachedResp)) {
20420 var xsrfValue = urlIsSameOrigin(config.url)
20421 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
20424 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
20427 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
20428 config.withCredentials, config.responseType);
20435 * Callback registered to $httpBackend():
20436 * - caches the response if desired
20437 * - resolves the raw $http promise
20440 function done(status, response, headersString, statusText) {
20442 if (isSuccess(status)) {
20443 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
20445 // remove promise from the cache
20450 function resolveHttpPromise() {
20451 resolvePromise(response, status, headersString, statusText);
20454 if (useApplyAsync) {
20455 $rootScope.$applyAsync(resolveHttpPromise);
20457 resolveHttpPromise();
20458 if (!$rootScope.$$phase) $rootScope.$apply();
20464 * Resolves the raw $http promise.
20466 function resolvePromise(response, status, headers, statusText) {
20467 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
20468 status = status >= -1 ? status : 0;
20470 (isSuccess(status) ? deferred.resolve : deferred.reject)({
20473 headers: headersGetter(headers),
20475 statusText: statusText
20479 function resolvePromiseWithResult(result) {
20480 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
20483 function removePendingReq() {
20484 var idx = $http.pendingRequests.indexOf(config);
20485 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
20490 function buildUrl(url, serializedParams) {
20491 if (serializedParams.length > 0) {
20492 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
20501 * @name $xhrFactory
20504 * Factory function used to create XMLHttpRequest objects.
20506 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
20509 * angular.module('myApp', [])
20510 * .factory('$xhrFactory', function() {
20511 * return function createXhr(method, url) {
20512 * return new window.XMLHttpRequest({mozSystem: true});
20517 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
20518 * @param {string} url URL of the request.
20520 function $xhrFactoryProvider() {
20521 this.$get = function() {
20522 return function createXhr() {
20523 return new window.XMLHttpRequest();
20530 * @name $httpBackend
20531 * @requires $window
20532 * @requires $document
20533 * @requires $xhrFactory
20536 * HTTP backend used by the {@link ng.$http service} that delegates to
20537 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
20539 * You should never need to use this service directly, instead use the higher-level abstractions:
20540 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
20542 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
20543 * $httpBackend} which can be trained with responses.
20545 function $HttpBackendProvider() {
20546 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
20547 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
20551 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
20552 // TODO(vojta): fix the signature
20553 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
20554 $browser.$$incOutstandingRequestCount();
20555 url = url || $browser.url();
20557 if (lowercase(method) == 'jsonp') {
20558 var callbackId = '_' + (callbacks.counter++).toString(36);
20559 callbacks[callbackId] = function(data) {
20560 callbacks[callbackId].data = data;
20561 callbacks[callbackId].called = true;
20564 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
20565 callbackId, function(status, text) {
20566 completeRequest(callback, status, callbacks[callbackId].data, "", text);
20567 callbacks[callbackId] = noop;
20571 var xhr = createXhr(method, url);
20573 xhr.open(method, url, true);
20574 forEach(headers, function(value, key) {
20575 if (isDefined(value)) {
20576 xhr.setRequestHeader(key, value);
20580 xhr.onload = function requestLoaded() {
20581 var statusText = xhr.statusText || '';
20583 // responseText is the old-school way of retrieving response (supported by IE9)
20584 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
20585 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
20587 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
20588 var status = xhr.status === 1223 ? 204 : xhr.status;
20590 // fix status code when it is 0 (0 status is undocumented).
20591 // Occurs when accessing file resources or on Android 4.1 stock browser
20592 // while retrieving files from application cache.
20593 if (status === 0) {
20594 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
20597 completeRequest(callback,
20600 xhr.getAllResponseHeaders(),
20604 var requestError = function() {
20605 // The response is always empty
20606 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
20607 completeRequest(callback, -1, null, null, '');
20610 xhr.onerror = requestError;
20611 xhr.onabort = requestError;
20613 if (withCredentials) {
20614 xhr.withCredentials = true;
20617 if (responseType) {
20619 xhr.responseType = responseType;
20621 // WebKit added support for the json responseType value on 09/03/2013
20622 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
20623 // known to throw when setting the value "json" as the response type. Other older
20624 // browsers implementing the responseType
20626 // The json response type can be ignored if not supported, because JSON payloads are
20627 // parsed on the client-side regardless.
20628 if (responseType !== 'json') {
20634 xhr.send(isUndefined(post) ? null : post);
20638 var timeoutId = $browserDefer(timeoutRequest, timeout);
20639 } else if (isPromiseLike(timeout)) {
20640 timeout.then(timeoutRequest);
20644 function timeoutRequest() {
20645 jsonpDone && jsonpDone();
20646 xhr && xhr.abort();
20649 function completeRequest(callback, status, response, headersString, statusText) {
20650 // cancel timeout and subsequent timeout promise resolution
20651 if (isDefined(timeoutId)) {
20652 $browserDefer.cancel(timeoutId);
20654 jsonpDone = xhr = null;
20656 callback(status, response, headersString, statusText);
20657 $browser.$$completeOutstandingRequest(noop);
20661 function jsonpReq(url, callbackId, done) {
20662 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
20663 // - fetches local scripts via XHR and evals them
20664 // - adds and immediately removes script elements from the document
20665 var script = rawDocument.createElement('script'), callback = null;
20666 script.type = "text/javascript";
20668 script.async = true;
20670 callback = function(event) {
20671 removeEventListenerFn(script, "load", callback);
20672 removeEventListenerFn(script, "error", callback);
20673 rawDocument.body.removeChild(script);
20676 var text = "unknown";
20679 if (event.type === "load" && !callbacks[callbackId].called) {
20680 event = { type: "error" };
20683 status = event.type === "error" ? 404 : 200;
20687 done(status, text);
20691 addEventListenerFn(script, "load", callback);
20692 addEventListenerFn(script, "error", callback);
20693 rawDocument.body.appendChild(script);
20698 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
20699 $interpolateMinErr.throwNoconcat = function(text) {
20700 throw $interpolateMinErr('noconcat',
20701 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
20702 "interpolations that concatenate multiple expressions when a trusted value is " +
20703 "required. See http://docs.angularjs.org/api/ng.$sce", text);
20706 $interpolateMinErr.interr = function(text, err) {
20707 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
20712 * @name $interpolateProvider
20716 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
20718 * <div class="alert alert-danger">
20719 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
20720 * template within a Python Jinja template (or any other template language). Mixing templating
20721 * languages is **very dangerous**. The embedding template language will not safely escape Angular
20722 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
20727 <example name="custom-interpolation-markup" module="customInterpolationApp">
20728 <file name="index.html">
20730 var customInterpolationApp = angular.module('customInterpolationApp', []);
20732 customInterpolationApp.config(function($interpolateProvider) {
20733 $interpolateProvider.startSymbol('//');
20734 $interpolateProvider.endSymbol('//');
20738 customInterpolationApp.controller('DemoController', function() {
20739 this.label = "This binding is brought you by // interpolation symbols.";
20742 <div ng-controller="DemoController as demo">
20746 <file name="protractor.js" type="protractor">
20747 it('should interpolate binding with custom symbols', function() {
20748 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
20753 function $InterpolateProvider() {
20754 var startSymbol = '{{';
20755 var endSymbol = '}}';
20759 * @name $interpolateProvider#startSymbol
20761 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
20763 * @param {string=} value new value to set the starting symbol to.
20764 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
20766 this.startSymbol = function(value) {
20768 startSymbol = value;
20771 return startSymbol;
20777 * @name $interpolateProvider#endSymbol
20779 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
20781 * @param {string=} value new value to set the ending symbol to.
20782 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
20784 this.endSymbol = function(value) {
20794 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
20795 var startSymbolLength = startSymbol.length,
20796 endSymbolLength = endSymbol.length,
20797 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
20798 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
20800 function escape(ch) {
20801 return '\\\\\\' + ch;
20804 function unescapeText(text) {
20805 return text.replace(escapedStartRegexp, startSymbol).
20806 replace(escapedEndRegexp, endSymbol);
20809 function stringify(value) {
20810 if (value == null) { // null || undefined
20813 switch (typeof value) {
20817 value = '' + value;
20820 value = toJson(value);
20826 //TODO: this is the same as the constantWatchDelegate in parse.js
20827 function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
20829 return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
20831 return constantInterp(scope);
20832 }, listener, objectEquality);
20837 * @name $interpolate
20845 * Compiles a string with markup into an interpolation function. This service is used by the
20846 * HTML {@link ng.$compile $compile} service for data binding. See
20847 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
20848 * interpolation markup.
20852 * var $interpolate = ...; // injected
20853 * var exp = $interpolate('Hello {{name | uppercase}}!');
20854 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
20857 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
20858 * `true`, the interpolation function will return `undefined` unless all embedded expressions
20859 * evaluate to a value other than `undefined`.
20862 * var $interpolate = ...; // injected
20863 * var context = {greeting: 'Hello', name: undefined };
20865 * // default "forgiving" mode
20866 * var exp = $interpolate('{{greeting}} {{name}}!');
20867 * expect(exp(context)).toEqual('Hello !');
20869 * // "allOrNothing" mode
20870 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
20871 * expect(exp(context)).toBeUndefined();
20872 * context.name = 'Angular';
20873 * expect(exp(context)).toEqual('Hello Angular!');
20876 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
20878 * ####Escaped Interpolation
20879 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
20880 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
20881 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
20884 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
20885 * degree, while also enabling code examples to work without relying on the
20886 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
20888 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
20889 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
20890 * interpolation start/end markers with their escaped counterparts.**
20892 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
20893 * output when the $interpolate service processes the text. So, for HTML elements interpolated
20894 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
20895 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
20896 * this is typically useful only when user-data is used in rendering a template from the server, or
20897 * when otherwise untrusted data is used by a directive.
20900 * <file name="index.html">
20901 * <div ng-init="username='A user'">
20902 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
20904 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
20905 * application, but fails to accomplish their task, because the server has correctly
20906 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
20908 * <p>Instead, the result of the attempted script injection is visible, and can be removed
20909 * from the database by an administrator.</p>
20914 * @param {string} text The text with markup to interpolate.
20915 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
20916 * embedded expression in order to return an interpolation function. Strings with no
20917 * embedded expression will return null for the interpolation function.
20918 * @param {string=} trustedContext when provided, the returned function passes the interpolated
20919 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
20920 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
20921 * provides Strict Contextual Escaping for details.
20922 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
20923 * unless all embedded expressions evaluate to a value other than `undefined`.
20924 * @returns {function(context)} an interpolation function which is used to compute the
20925 * interpolated string. The function has these parameters:
20927 * - `context`: evaluation context for all expressions embedded in the interpolated text
20929 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
20930 // Provide a quick exit and simplified result function for text with no interpolation
20931 if (!text.length || text.indexOf(startSymbol) === -1) {
20932 var constantInterp;
20933 if (!mustHaveExpression) {
20934 var unescapedText = unescapeText(text);
20935 constantInterp = valueFn(unescapedText);
20936 constantInterp.exp = text;
20937 constantInterp.expressions = [];
20938 constantInterp.$$watchDelegate = constantWatchDelegate;
20940 return constantInterp;
20943 allOrNothing = !!allOrNothing;
20949 textLength = text.length,
20952 expressionPositions = [];
20954 while (index < textLength) {
20955 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
20956 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
20957 if (index !== startIndex) {
20958 concat.push(unescapeText(text.substring(index, startIndex)));
20960 exp = text.substring(startIndex + startSymbolLength, endIndex);
20961 expressions.push(exp);
20962 parseFns.push($parse(exp, parseStringifyInterceptor));
20963 index = endIndex + endSymbolLength;
20964 expressionPositions.push(concat.length);
20967 // we did not find an interpolation, so we have to add the remainder to the separators array
20968 if (index !== textLength) {
20969 concat.push(unescapeText(text.substring(index)));
20975 // Concatenating expressions makes it hard to reason about whether some combination of
20976 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
20977 // single expression be used for iframe[src], object[src], etc., we ensure that the value
20978 // that's used is assigned or constructed by some JS code somewhere that is more testable or
20979 // make it obvious that you bound the value to some user controlled value. This helps reduce
20980 // the load when auditing for XSS issues.
20981 if (trustedContext && concat.length > 1) {
20982 $interpolateMinErr.throwNoconcat(text);
20985 if (!mustHaveExpression || expressions.length) {
20986 var compute = function(values) {
20987 for (var i = 0, ii = expressions.length; i < ii; i++) {
20988 if (allOrNothing && isUndefined(values[i])) return;
20989 concat[expressionPositions[i]] = values[i];
20991 return concat.join('');
20994 var getValue = function(value) {
20995 return trustedContext ?
20996 $sce.getTrusted(trustedContext, value) :
20997 $sce.valueOf(value);
21000 return extend(function interpolationFn(context) {
21002 var ii = expressions.length;
21003 var values = new Array(ii);
21006 for (; i < ii; i++) {
21007 values[i] = parseFns[i](context);
21010 return compute(values);
21012 $exceptionHandler($interpolateMinErr.interr(text, err));
21016 // all of these properties are undocumented for now
21017 exp: text, //just for compatibility with regular watchers created via $watch
21018 expressions: expressions,
21019 $$watchDelegate: function(scope, listener) {
21021 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
21022 var currValue = compute(values);
21023 if (isFunction(listener)) {
21024 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
21026 lastValue = currValue;
21032 function parseStringifyInterceptor(value) {
21034 value = getValue(value);
21035 return allOrNothing && !isDefined(value) ? value : stringify(value);
21037 $exceptionHandler($interpolateMinErr.interr(text, err));
21045 * @name $interpolate#startSymbol
21047 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
21049 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
21052 * @returns {string} start symbol.
21054 $interpolate.startSymbol = function() {
21055 return startSymbol;
21061 * @name $interpolate#endSymbol
21063 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
21065 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
21068 * @returns {string} end symbol.
21070 $interpolate.endSymbol = function() {
21074 return $interpolate;
21078 function $IntervalProvider() {
21079 this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
21080 function($rootScope, $window, $q, $$q, $browser) {
21081 var intervals = {};
21089 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
21092 * The return value of registering an interval function is a promise. This promise will be
21093 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
21094 * run indefinitely if `count` is not defined. The value of the notification will be the
21095 * number of iterations that have run.
21096 * To cancel an interval, call `$interval.cancel(promise)`.
21098 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
21099 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
21102 * <div class="alert alert-warning">
21103 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
21104 * with them. In particular they are not automatically destroyed when a controller's scope or a
21105 * directive's element are destroyed.
21106 * You should take this into consideration and make sure to always cancel the interval at the
21107 * appropriate moment. See the example below for more details on how and when to do this.
21110 * @param {function()} fn A function that should be called repeatedly.
21111 * @param {number} delay Number of milliseconds between each function call.
21112 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
21114 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
21115 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
21116 * @param {...*=} Pass additional parameters to the executed function.
21117 * @returns {promise} A promise which will be notified on each iteration.
21120 * <example module="intervalExample">
21121 * <file name="index.html">
21123 * angular.module('intervalExample', [])
21124 * .controller('ExampleController', ['$scope', '$interval',
21125 * function($scope, $interval) {
21126 * $scope.format = 'M/d/yy h:mm:ss a';
21127 * $scope.blood_1 = 100;
21128 * $scope.blood_2 = 120;
21131 * $scope.fight = function() {
21132 * // Don't start a new fight if we are already fighting
21133 * if ( angular.isDefined(stop) ) return;
21135 * stop = $interval(function() {
21136 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
21137 * $scope.blood_1 = $scope.blood_1 - 3;
21138 * $scope.blood_2 = $scope.blood_2 - 4;
21140 * $scope.stopFight();
21145 * $scope.stopFight = function() {
21146 * if (angular.isDefined(stop)) {
21147 * $interval.cancel(stop);
21148 * stop = undefined;
21152 * $scope.resetFight = function() {
21153 * $scope.blood_1 = 100;
21154 * $scope.blood_2 = 120;
21157 * $scope.$on('$destroy', function() {
21158 * // Make sure that the interval is destroyed too
21159 * $scope.stopFight();
21162 * // Register the 'myCurrentTime' directive factory method.
21163 * // We inject $interval and dateFilter service since the factory method is DI.
21164 * .directive('myCurrentTime', ['$interval', 'dateFilter',
21165 * function($interval, dateFilter) {
21166 * // return the directive link function. (compile function not needed)
21167 * return function(scope, element, attrs) {
21168 * var format, // date format
21169 * stopTime; // so that we can cancel the time updates
21171 * // used to update the UI
21172 * function updateTime() {
21173 * element.text(dateFilter(new Date(), format));
21176 * // watch the expression, and update the UI on change.
21177 * scope.$watch(attrs.myCurrentTime, function(value) {
21182 * stopTime = $interval(updateTime, 1000);
21184 * // listen on DOM destroy (removal) event, and cancel the next UI update
21185 * // to prevent updating time after the DOM element was removed.
21186 * element.on('$destroy', function() {
21187 * $interval.cancel(stopTime);
21194 * <div ng-controller="ExampleController">
21195 * <label>Date format: <input ng-model="format"></label> <hr/>
21196 * Current time is: <span my-current-time="format"></span>
21198 * Blood 1 : <font color='red'>{{blood_1}}</font>
21199 * Blood 2 : <font color='red'>{{blood_2}}</font>
21200 * <button type="button" data-ng-click="fight()">Fight</button>
21201 * <button type="button" data-ng-click="stopFight()">StopFight</button>
21202 * <button type="button" data-ng-click="resetFight()">resetFight</button>
21209 function interval(fn, delay, count, invokeApply) {
21210 var hasParams = arguments.length > 4,
21211 args = hasParams ? sliceArgs(arguments, 4) : [],
21212 setInterval = $window.setInterval,
21213 clearInterval = $window.clearInterval,
21215 skipApply = (isDefined(invokeApply) && !invokeApply),
21216 deferred = (skipApply ? $$q : $q).defer(),
21217 promise = deferred.promise;
21219 count = isDefined(count) ? count : 0;
21221 promise.$$intervalId = setInterval(function tick() {
21223 $browser.defer(callback);
21225 $rootScope.$evalAsync(callback);
21227 deferred.notify(iteration++);
21229 if (count > 0 && iteration >= count) {
21230 deferred.resolve(iteration);
21231 clearInterval(promise.$$intervalId);
21232 delete intervals[promise.$$intervalId];
21235 if (!skipApply) $rootScope.$apply();
21239 intervals[promise.$$intervalId] = deferred;
21243 function callback() {
21247 fn.apply(null, args);
21255 * @name $interval#cancel
21258 * Cancels a task associated with the `promise`.
21260 * @param {Promise=} promise returned by the `$interval` function.
21261 * @returns {boolean} Returns `true` if the task was successfully canceled.
21263 interval.cancel = function(promise) {
21264 if (promise && promise.$$intervalId in intervals) {
21265 intervals[promise.$$intervalId].reject('canceled');
21266 $window.clearInterval(promise.$$intervalId);
21267 delete intervals[promise.$$intervalId];
21282 * $locale service provides localization rules for various Angular components. As of right now the
21283 * only public api is:
21285 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
21288 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
21289 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
21290 var $locationMinErr = minErr('$location');
21294 * Encode path using encodeUriSegment, ignoring forward slashes
21296 * @param {string} path Path to encode
21297 * @returns {string}
21299 function encodePath(path) {
21300 var segments = path.split('/'),
21301 i = segments.length;
21304 segments[i] = encodeUriSegment(segments[i]);
21307 return segments.join('/');
21310 function parseAbsoluteUrl(absoluteUrl, locationObj) {
21311 var parsedUrl = urlResolve(absoluteUrl);
21313 locationObj.$$protocol = parsedUrl.protocol;
21314 locationObj.$$host = parsedUrl.hostname;
21315 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
21319 function parseAppUrl(relativeUrl, locationObj) {
21320 var prefixed = (relativeUrl.charAt(0) !== '/');
21322 relativeUrl = '/' + relativeUrl;
21324 var match = urlResolve(relativeUrl);
21325 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
21326 match.pathname.substring(1) : match.pathname);
21327 locationObj.$$search = parseKeyValue(match.search);
21328 locationObj.$$hash = decodeURIComponent(match.hash);
21330 // make sure path starts with '/';
21331 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
21332 locationObj.$$path = '/' + locationObj.$$path;
21339 * @param {string} begin
21340 * @param {string} whole
21341 * @returns {string} returns text from whole after begin or undefined if it does not begin with
21344 function beginsWith(begin, whole) {
21345 if (whole.indexOf(begin) === 0) {
21346 return whole.substr(begin.length);
21351 function stripHash(url) {
21352 var index = url.indexOf('#');
21353 return index == -1 ? url : url.substr(0, index);
21356 function trimEmptyHash(url) {
21357 return url.replace(/(#.+)|#$/, '$1');
21361 function stripFile(url) {
21362 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
21365 /* return the server only (scheme://host:port) */
21366 function serverBase(url) {
21367 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
21372 * LocationHtml5Url represents an url
21373 * This object is exposed as $location service when HTML5 mode is enabled and supported
21376 * @param {string} appBase application base URL
21377 * @param {string} appBaseNoFile application base URL stripped of any filename
21378 * @param {string} basePrefix url path prefix
21380 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
21381 this.$$html5 = true;
21382 basePrefix = basePrefix || '';
21383 parseAbsoluteUrl(appBase, this);
21387 * Parse given html5 (regular) url string into properties
21388 * @param {string} url HTML5 url
21391 this.$$parse = function(url) {
21392 var pathUrl = beginsWith(appBaseNoFile, url);
21393 if (!isString(pathUrl)) {
21394 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
21398 parseAppUrl(pathUrl, this);
21400 if (!this.$$path) {
21408 * Compose url and update `absUrl` property
21411 this.$$compose = function() {
21412 var search = toKeyValue(this.$$search),
21413 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
21415 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
21416 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
21419 this.$$parseLinkUrl = function(url, relHref) {
21420 if (relHref && relHref[0] === '#') {
21421 // special case for links to hash fragments:
21422 // keep the old url and only replace the hash fragment
21423 this.hash(relHref.slice(1));
21426 var appUrl, prevAppUrl;
21429 if (isDefined(appUrl = beginsWith(appBase, url))) {
21430 prevAppUrl = appUrl;
21431 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
21432 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
21434 rewrittenUrl = appBase + prevAppUrl;
21436 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
21437 rewrittenUrl = appBaseNoFile + appUrl;
21438 } else if (appBaseNoFile == url + '/') {
21439 rewrittenUrl = appBaseNoFile;
21441 if (rewrittenUrl) {
21442 this.$$parse(rewrittenUrl);
21444 return !!rewrittenUrl;
21450 * LocationHashbangUrl represents url
21451 * This object is exposed as $location service when developer doesn't opt into html5 mode.
21452 * It also serves as the base class for html5 mode fallback on legacy browsers.
21455 * @param {string} appBase application base URL
21456 * @param {string} appBaseNoFile application base URL stripped of any filename
21457 * @param {string} hashPrefix hashbang prefix
21459 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
21461 parseAbsoluteUrl(appBase, this);
21465 * Parse given hashbang url into properties
21466 * @param {string} url Hashbang url
21469 this.$$parse = function(url) {
21470 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
21471 var withoutHashUrl;
21473 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
21475 // The rest of the url starts with a hash so we have
21476 // got either a hashbang path or a plain hash fragment
21477 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
21478 if (isUndefined(withoutHashUrl)) {
21479 // There was no hashbang prefix so we just have a hash fragment
21480 withoutHashUrl = withoutBaseUrl;
21484 // There was no hashbang path nor hash fragment:
21485 // If we are in HTML5 mode we use what is left as the path;
21486 // Otherwise we ignore what is left
21487 if (this.$$html5) {
21488 withoutHashUrl = withoutBaseUrl;
21490 withoutHashUrl = '';
21491 if (isUndefined(withoutBaseUrl)) {
21498 parseAppUrl(withoutHashUrl, this);
21500 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
21505 * In Windows, on an anchor node on documents loaded from
21506 * the filesystem, the browser will return a pathname
21507 * prefixed with the drive name ('/C:/path') when a
21508 * pathname without a drive is set:
21509 * * a.setAttribute('href', '/foo')
21510 * * a.pathname === '/C:/foo' //true
21512 * Inside of Angular, we're always using pathnames that
21513 * do not include drive names for routing.
21515 function removeWindowsDriveName(path, url, base) {
21517 Matches paths for file protocol on windows,
21518 such as /C:/foo/bar, and captures only /foo/bar.
21520 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
21522 var firstPathSegmentMatch;
21524 //Get the relative path from the input URL.
21525 if (url.indexOf(base) === 0) {
21526 url = url.replace(base, '');
21529 // The input URL intentionally contains a first path segment that ends with a colon.
21530 if (windowsFilePathExp.exec(url)) {
21534 firstPathSegmentMatch = windowsFilePathExp.exec(path);
21535 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
21540 * Compose hashbang url and update `absUrl` property
21543 this.$$compose = function() {
21544 var search = toKeyValue(this.$$search),
21545 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
21547 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
21548 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
21551 this.$$parseLinkUrl = function(url, relHref) {
21552 if (stripHash(appBase) == stripHash(url)) {
21562 * LocationHashbangUrl represents url
21563 * This object is exposed as $location service when html5 history api is enabled but the browser
21564 * does not support it.
21567 * @param {string} appBase application base URL
21568 * @param {string} appBaseNoFile application base URL stripped of any filename
21569 * @param {string} hashPrefix hashbang prefix
21571 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
21572 this.$$html5 = true;
21573 LocationHashbangUrl.apply(this, arguments);
21575 this.$$parseLinkUrl = function(url, relHref) {
21576 if (relHref && relHref[0] === '#') {
21577 // special case for links to hash fragments:
21578 // keep the old url and only replace the hash fragment
21579 this.hash(relHref.slice(1));
21586 if (appBase == stripHash(url)) {
21587 rewrittenUrl = url;
21588 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
21589 rewrittenUrl = appBase + hashPrefix + appUrl;
21590 } else if (appBaseNoFile === url + '/') {
21591 rewrittenUrl = appBaseNoFile;
21593 if (rewrittenUrl) {
21594 this.$$parse(rewrittenUrl);
21596 return !!rewrittenUrl;
21599 this.$$compose = function() {
21600 var search = toKeyValue(this.$$search),
21601 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
21603 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
21604 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
21605 this.$$absUrl = appBase + hashPrefix + this.$$url;
21611 var locationPrototype = {
21614 * Are we in html5 mode?
21620 * Has any change been replacing?
21627 * @name $location#absUrl
21630 * This method is getter only.
21632 * Return full url representation with all segments encoded according to rules specified in
21633 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
21637 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21638 * var absUrl = $location.absUrl();
21639 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
21642 * @return {string} full url
21644 absUrl: locationGetter('$$absUrl'),
21648 * @name $location#url
21651 * This method is getter / setter.
21653 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
21655 * Change path, search and hash, when called with parameter and return `$location`.
21659 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21660 * var url = $location.url();
21661 * // => "/some/path?foo=bar&baz=xoxo"
21664 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
21665 * @return {string} url
21667 url: function(url) {
21668 if (isUndefined(url)) {
21672 var match = PATH_MATCH.exec(url);
21673 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
21674 if (match[2] || match[1] || url === '') this.search(match[3] || '');
21675 this.hash(match[5] || '');
21682 * @name $location#protocol
21685 * This method is getter only.
21687 * Return protocol of current url.
21691 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21692 * var protocol = $location.protocol();
21696 * @return {string} protocol of current url
21698 protocol: locationGetter('$$protocol'),
21702 * @name $location#host
21705 * This method is getter only.
21707 * Return host of current url.
21709 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
21713 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21714 * var host = $location.host();
21715 * // => "example.com"
21717 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
21718 * host = $location.host();
21719 * // => "example.com"
21720 * host = location.host;
21721 * // => "example.com:8080"
21724 * @return {string} host of current url.
21726 host: locationGetter('$$host'),
21730 * @name $location#port
21733 * This method is getter only.
21735 * Return port of current url.
21739 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21740 * var port = $location.port();
21744 * @return {Number} port
21746 port: locationGetter('$$port'),
21750 * @name $location#path
21753 * This method is getter / setter.
21755 * Return path of current url when called without any parameter.
21757 * Change path when called with parameter and return `$location`.
21759 * Note: Path should always begin with forward slash (/), this method will add the forward slash
21760 * if it is missing.
21764 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21765 * var path = $location.path();
21766 * // => "/some/path"
21769 * @param {(string|number)=} path New path
21770 * @return {string} path
21772 path: locationGetterSetter('$$path', function(path) {
21773 path = path !== null ? path.toString() : '';
21774 return path.charAt(0) == '/' ? path : '/' + path;
21779 * @name $location#search
21782 * This method is getter / setter.
21784 * Return search part (as object) of current url when called without any parameter.
21786 * Change search part when called with parameter and return `$location`.
21790 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
21791 * var searchObject = $location.search();
21792 * // => {foo: 'bar', baz: 'xoxo'}
21794 * // set foo to 'yipee'
21795 * $location.search('foo', 'yipee');
21796 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
21799 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
21802 * When called with a single argument the method acts as a setter, setting the `search` component
21803 * of `$location` to the specified value.
21805 * If the argument is a hash object containing an array of values, these values will be encoded
21806 * as duplicate search parameters in the url.
21808 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
21809 * will override only a single search property.
21811 * If `paramValue` is an array, it will override the property of the `search` component of
21812 * `$location` specified via the first argument.
21814 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
21816 * If `paramValue` is `true`, the property specified via the first argument will be added with no
21817 * value nor trailing equal sign.
21819 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
21820 * one or more arguments returns `$location` object itself.
21822 search: function(search, paramValue) {
21823 switch (arguments.length) {
21825 return this.$$search;
21827 if (isString(search) || isNumber(search)) {
21828 search = search.toString();
21829 this.$$search = parseKeyValue(search);
21830 } else if (isObject(search)) {
21831 search = copy(search, {});
21832 // remove object undefined or null properties
21833 forEach(search, function(value, key) {
21834 if (value == null) delete search[key];
21837 this.$$search = search;
21839 throw $locationMinErr('isrcharg',
21840 'The first argument of the `$location#search()` call must be a string or an object.');
21844 if (isUndefined(paramValue) || paramValue === null) {
21845 delete this.$$search[search];
21847 this.$$search[search] = paramValue;
21857 * @name $location#hash
21860 * This method is getter / setter.
21862 * Returns the hash fragment when called without any parameters.
21864 * Changes the hash fragment when called with a parameter and returns `$location`.
21868 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
21869 * var hash = $location.hash();
21870 * // => "hashValue"
21873 * @param {(string|number)=} hash New hash fragment
21874 * @return {string} hash
21876 hash: locationGetterSetter('$$hash', function(hash) {
21877 return hash !== null ? hash.toString() : '';
21882 * @name $location#replace
21885 * If called, all changes to $location during the current `$digest` will replace the current history
21886 * record, instead of adding a new one.
21888 replace: function() {
21889 this.$$replace = true;
21894 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
21895 Location.prototype = Object.create(locationPrototype);
21899 * @name $location#state
21902 * This method is getter / setter.
21904 * Return the history state object when called without any parameter.
21906 * Change the history state object when called with one parameter and return `$location`.
21907 * The state object is later passed to `pushState` or `replaceState`.
21909 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
21910 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
21911 * older browsers (like IE9 or Android < 4.0), don't use this method.
21913 * @param {object=} state State object for pushState or replaceState
21914 * @return {object} state
21916 Location.prototype.state = function(state) {
21917 if (!arguments.length) {
21918 return this.$$state;
21921 if (Location !== LocationHtml5Url || !this.$$html5) {
21922 throw $locationMinErr('nostate', 'History API state support is available only ' +
21923 'in HTML5 mode and only in browsers supporting HTML5 History API');
21925 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
21926 // but we're changing the $$state reference to $browser.state() during the $digest
21927 // so the modification window is narrow.
21928 this.$$state = isUndefined(state) ? null : state;
21935 function locationGetter(property) {
21936 return function() {
21937 return this[property];
21942 function locationGetterSetter(property, preprocess) {
21943 return function(value) {
21944 if (isUndefined(value)) {
21945 return this[property];
21948 this[property] = preprocess(value);
21960 * @requires $rootElement
21963 * The $location service parses the URL in the browser address bar (based on the
21964 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
21965 * available to your application. Changes to the URL in the address bar are reflected into
21966 * $location service and changes to $location are reflected into the browser address bar.
21968 * **The $location service:**
21970 * - Exposes the current URL in the browser address bar, so you can
21971 * - Watch and observe the URL.
21972 * - Change the URL.
21973 * - Synchronizes the URL with the browser when the user
21974 * - Changes the address bar.
21975 * - Clicks the back or forward button (or clicks a History link).
21976 * - Clicks on a link.
21977 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
21979 * For more information see {@link guide/$location Developer Guide: Using $location}
21984 * @name $locationProvider
21986 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
21988 function $LocationProvider() {
21989 var hashPrefix = '',
21998 * @name $locationProvider#hashPrefix
22000 * @param {string=} prefix Prefix for hash part (containing path and search)
22001 * @returns {*} current value if used as getter or itself (chaining) if used as setter
22003 this.hashPrefix = function(prefix) {
22004 if (isDefined(prefix)) {
22005 hashPrefix = prefix;
22014 * @name $locationProvider#html5Mode
22016 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
22017 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
22019 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
22020 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
22021 * support `pushState`.
22022 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
22023 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
22024 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
22025 * See the {@link guide/$location $location guide for more information}
22026 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
22027 * enables/disables url rewriting for relative links.
22029 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
22031 this.html5Mode = function(mode) {
22032 if (isBoolean(mode)) {
22033 html5Mode.enabled = mode;
22035 } else if (isObject(mode)) {
22037 if (isBoolean(mode.enabled)) {
22038 html5Mode.enabled = mode.enabled;
22041 if (isBoolean(mode.requireBase)) {
22042 html5Mode.requireBase = mode.requireBase;
22045 if (isBoolean(mode.rewriteLinks)) {
22046 html5Mode.rewriteLinks = mode.rewriteLinks;
22057 * @name $location#$locationChangeStart
22058 * @eventType broadcast on root scope
22060 * Broadcasted before a URL will change.
22062 * This change can be prevented by calling
22063 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
22064 * details about event object. Upon successful change
22065 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
22067 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
22068 * the browser supports the HTML5 History API.
22070 * @param {Object} angularEvent Synthetic event object.
22071 * @param {string} newUrl New URL
22072 * @param {string=} oldUrl URL that was before it was changed.
22073 * @param {string=} newState New history state object
22074 * @param {string=} oldState History state object that was before it was changed.
22079 * @name $location#$locationChangeSuccess
22080 * @eventType broadcast on root scope
22082 * Broadcasted after a URL was changed.
22084 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
22085 * the browser supports the HTML5 History API.
22087 * @param {Object} angularEvent Synthetic event object.
22088 * @param {string} newUrl New URL
22089 * @param {string=} oldUrl URL that was before it was changed.
22090 * @param {string=} newState New history state object
22091 * @param {string=} oldState History state object that was before it was changed.
22094 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
22095 function($rootScope, $browser, $sniffer, $rootElement, $window) {
22098 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
22099 initialUrl = $browser.url(),
22102 if (html5Mode.enabled) {
22103 if (!baseHref && html5Mode.requireBase) {
22104 throw $locationMinErr('nobase',
22105 "$location in HTML5 mode requires a <base> tag to be present!");
22107 appBase = serverBase(initialUrl) + (baseHref || '/');
22108 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
22110 appBase = stripHash(initialUrl);
22111 LocationMode = LocationHashbangUrl;
22113 var appBaseNoFile = stripFile(appBase);
22115 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
22116 $location.$$parseLinkUrl(initialUrl, initialUrl);
22118 $location.$$state = $browser.state();
22120 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
22122 function setBrowserUrlWithFallback(url, replace, state) {
22123 var oldUrl = $location.url();
22124 var oldState = $location.$$state;
22126 $browser.url(url, replace, state);
22128 // Make sure $location.state() returns referentially identical (not just deeply equal)
22129 // state object; this makes possible quick checking if the state changed in the digest
22130 // loop. Checking deep equality would be too expensive.
22131 $location.$$state = $browser.state();
22133 // Restore old values if pushState fails
22134 $location.url(oldUrl);
22135 $location.$$state = oldState;
22141 $rootElement.on('click', function(event) {
22142 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
22143 // currently we open nice url link and redirect then
22145 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
22147 var elm = jqLite(event.target);
22149 // traverse the DOM up to find first A tag
22150 while (nodeName_(elm[0]) !== 'a') {
22151 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
22152 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
22155 var absHref = elm.prop('href');
22156 // get the actual href attribute - see
22157 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
22158 var relHref = elm.attr('href') || elm.attr('xlink:href');
22160 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
22161 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
22163 absHref = urlResolve(absHref.animVal).href;
22166 // Ignore when url is started with javascript: or mailto:
22167 if (IGNORE_URI_REGEXP.test(absHref)) return;
22169 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
22170 if ($location.$$parseLinkUrl(absHref, relHref)) {
22171 // We do a preventDefault for all urls that are part of the angular application,
22172 // in html5mode and also without, so that we are able to abort navigation without
22173 // getting double entries in the location history.
22174 event.preventDefault();
22175 // update location manually
22176 if ($location.absUrl() != $browser.url()) {
22177 $rootScope.$apply();
22178 // hack to work around FF6 bug 684208 when scenario runner clicks on links
22179 $window.angular['ff-684208-preventDefault'] = true;
22186 // rewrite hashbang url <> html5 url
22187 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
22188 $browser.url($location.absUrl(), true);
22191 var initializing = true;
22193 // update $location when $browser url changes
22194 $browser.onUrlChange(function(newUrl, newState) {
22196 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
22197 // If we are navigating outside of the app then force a reload
22198 $window.location.href = newUrl;
22202 $rootScope.$evalAsync(function() {
22203 var oldUrl = $location.absUrl();
22204 var oldState = $location.$$state;
22205 var defaultPrevented;
22206 newUrl = trimEmptyHash(newUrl);
22207 $location.$$parse(newUrl);
22208 $location.$$state = newState;
22210 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
22211 newState, oldState).defaultPrevented;
22213 // if the location was changed by a `$locationChangeStart` handler then stop
22214 // processing this location change
22215 if ($location.absUrl() !== newUrl) return;
22217 if (defaultPrevented) {
22218 $location.$$parse(oldUrl);
22219 $location.$$state = oldState;
22220 setBrowserUrlWithFallback(oldUrl, false, oldState);
22222 initializing = false;
22223 afterLocationChange(oldUrl, oldState);
22226 if (!$rootScope.$$phase) $rootScope.$digest();
22230 $rootScope.$watch(function $locationWatch() {
22231 var oldUrl = trimEmptyHash($browser.url());
22232 var newUrl = trimEmptyHash($location.absUrl());
22233 var oldState = $browser.state();
22234 var currentReplace = $location.$$replace;
22235 var urlOrStateChanged = oldUrl !== newUrl ||
22236 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
22238 if (initializing || urlOrStateChanged) {
22239 initializing = false;
22241 $rootScope.$evalAsync(function() {
22242 var newUrl = $location.absUrl();
22243 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
22244 $location.$$state, oldState).defaultPrevented;
22246 // if the location was changed by a `$locationChangeStart` handler then stop
22247 // processing this location change
22248 if ($location.absUrl() !== newUrl) return;
22250 if (defaultPrevented) {
22251 $location.$$parse(oldUrl);
22252 $location.$$state = oldState;
22254 if (urlOrStateChanged) {
22255 setBrowserUrlWithFallback(newUrl, currentReplace,
22256 oldState === $location.$$state ? null : $location.$$state);
22258 afterLocationChange(oldUrl, oldState);
22263 $location.$$replace = false;
22265 // we don't need to return anything because $evalAsync will make the digest loop dirty when
22266 // there is a change
22271 function afterLocationChange(oldUrl, oldState) {
22272 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
22273 $location.$$state, oldState);
22281 * @requires $window
22284 * Simple service for logging. Default implementation safely writes the message
22285 * into the browser's console (if present).
22287 * The main purpose of this service is to simplify debugging and troubleshooting.
22289 * The default is to log `debug` messages. You can use
22290 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
22293 <example module="logExample">
22294 <file name="script.js">
22295 angular.module('logExample', [])
22296 .controller('LogController', ['$scope', '$log', function($scope, $log) {
22297 $scope.$log = $log;
22298 $scope.message = 'Hello World!';
22301 <file name="index.html">
22302 <div ng-controller="LogController">
22303 <p>Reload this page with open console, enter text and hit the log button...</p>
22305 <input type="text" ng-model="message" /></label>
22306 <button ng-click="$log.log(message)">log</button>
22307 <button ng-click="$log.warn(message)">warn</button>
22308 <button ng-click="$log.info(message)">info</button>
22309 <button ng-click="$log.error(message)">error</button>
22310 <button ng-click="$log.debug(message)">debug</button>
22318 * @name $logProvider
22320 * Use the `$logProvider` to configure how the application logs messages
22322 function $LogProvider() {
22328 * @name $logProvider#debugEnabled
22330 * @param {boolean=} flag enable or disable debug level messages
22331 * @returns {*} current value if used as getter or itself (chaining) if used as setter
22333 this.debugEnabled = function(flag) {
22334 if (isDefined(flag)) {
22342 this.$get = ['$window', function($window) {
22349 * Write a log message
22351 log: consoleLog('log'),
22358 * Write an information message
22360 info: consoleLog('info'),
22367 * Write a warning message
22369 warn: consoleLog('warn'),
22376 * Write an error message
22378 error: consoleLog('error'),
22385 * Write a debug message
22387 debug: (function() {
22388 var fn = consoleLog('debug');
22390 return function() {
22392 fn.apply(self, arguments);
22398 function formatError(arg) {
22399 if (arg instanceof Error) {
22401 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
22402 ? 'Error: ' + arg.message + '\n' + arg.stack
22404 } else if (arg.sourceURL) {
22405 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
22411 function consoleLog(type) {
22412 var console = $window.console || {},
22413 logFn = console[type] || console.log || noop,
22416 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
22417 // The reason behind this is that console.log has type "object" in IE8...
22419 hasApply = !!logFn.apply;
22423 return function() {
22425 forEach(arguments, function(arg) {
22426 args.push(formatError(arg));
22428 return logFn.apply(console, args);
22432 // we are IE which either doesn't have window.console => this is noop and we do nothing,
22433 // or we are IE where console.log doesn't have apply so we log at least first 2 args
22434 return function(arg1, arg2) {
22435 logFn(arg1, arg2 == null ? '' : arg2);
22441 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22442 * Any commits to this file should be reviewed with security in mind. *
22443 * Changes to this file can potentially create security vulnerabilities. *
22444 * An approval from 2 Core members with history of modifying *
22445 * this file is required. *
22447 * Does the change somehow allow for arbitrary javascript to be executed? *
22448 * Or allows for someone to change the prototype of built-in objects? *
22449 * Or gives undesired access to variables likes document or window? *
22450 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22452 var $parseMinErr = minErr('$parse');
22454 // Sandboxing Angular Expressions
22455 // ------------------------------
22456 // Angular expressions are generally considered safe because these expressions only have direct
22457 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
22458 // obtaining a reference to native JS functions such as the Function constructor.
22460 // As an example, consider the following Angular expression:
22462 // {}.toString.constructor('alert("evil JS code")')
22464 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
22465 // against the expression language, but not to prevent exploits that were enabled by exposing
22466 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
22467 // practice and therefore we are not even trying to protect against interaction with an object
22468 // explicitly exposed in this way.
22470 // In general, it is not possible to access a Window object from an angular expression unless a
22471 // window or some DOM object that has a reference to window is published onto a Scope.
22472 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
22475 // See https://docs.angularjs.org/guide/security
22478 function ensureSafeMemberName(name, fullExpression) {
22479 if (name === "__defineGetter__" || name === "__defineSetter__"
22480 || name === "__lookupGetter__" || name === "__lookupSetter__"
22481 || name === "__proto__") {
22482 throw $parseMinErr('isecfld',
22483 'Attempting to access a disallowed field in Angular expressions! '
22484 + 'Expression: {0}', fullExpression);
22489 function getStringValue(name) {
22490 // Property names must be strings. This means that non-string objects cannot be used
22491 // as keys in an object. Any non-string object, including a number, is typecasted
22492 // into a string via the toString method.
22493 // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
22495 // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
22496 // to a string. It's not always possible. If `name` is an object and its `toString` method is
22497 // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
22499 // TypeError: Cannot convert object to primitive value
22501 // For performance reasons, we don't catch this error here and allow it to propagate up the call
22502 // stack. Note that you'll get the same error in JavaScript if you try to access a property using
22503 // such a 'broken' object as a key.
22507 function ensureSafeObject(obj, fullExpression) {
22508 // nifty check if obj is Function that is fast and works across iframes and other contexts
22510 if (obj.constructor === obj) {
22511 throw $parseMinErr('isecfn',
22512 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
22514 } else if (// isWindow(obj)
22515 obj.window === obj) {
22516 throw $parseMinErr('isecwindow',
22517 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
22519 } else if (// isElement(obj)
22520 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
22521 throw $parseMinErr('isecdom',
22522 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
22524 } else if (// block Object so that we can't get hold of dangerous Object.* methods
22526 throw $parseMinErr('isecobj',
22527 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
22534 var CALL = Function.prototype.call;
22535 var APPLY = Function.prototype.apply;
22536 var BIND = Function.prototype.bind;
22538 function ensureSafeFunction(obj, fullExpression) {
22540 if (obj.constructor === obj) {
22541 throw $parseMinErr('isecfn',
22542 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
22544 } else if (obj === CALL || obj === APPLY || obj === BIND) {
22545 throw $parseMinErr('isecff',
22546 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
22552 function ensureSafeAssignContext(obj, fullExpression) {
22554 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
22555 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
22556 throw $parseMinErr('isecaf',
22557 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
22562 var OPERATORS = createMap();
22563 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
22564 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
22567 /////////////////////////////////////////
22573 var Lexer = function(options) {
22574 this.options = options;
22577 Lexer.prototype = {
22578 constructor: Lexer,
22580 lex: function(text) {
22585 while (this.index < this.text.length) {
22586 var ch = this.text.charAt(this.index);
22587 if (ch === '"' || ch === "'") {
22588 this.readString(ch);
22589 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
22591 } else if (this.isIdent(ch)) {
22593 } else if (this.is(ch, '(){}[].,;:?')) {
22594 this.tokens.push({index: this.index, text: ch});
22596 } else if (this.isWhitespace(ch)) {
22599 var ch2 = ch + this.peek();
22600 var ch3 = ch2 + this.peek(2);
22601 var op1 = OPERATORS[ch];
22602 var op2 = OPERATORS[ch2];
22603 var op3 = OPERATORS[ch3];
22604 if (op1 || op2 || op3) {
22605 var token = op3 ? ch3 : (op2 ? ch2 : ch);
22606 this.tokens.push({index: this.index, text: token, operator: true});
22607 this.index += token.length;
22609 this.throwError('Unexpected next character ', this.index, this.index + 1);
22613 return this.tokens;
22616 is: function(ch, chars) {
22617 return chars.indexOf(ch) !== -1;
22620 peek: function(i) {
22622 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
22625 isNumber: function(ch) {
22626 return ('0' <= ch && ch <= '9') && typeof ch === "string";
22629 isWhitespace: function(ch) {
22630 // IE treats non-breaking space as \u00A0
22631 return (ch === ' ' || ch === '\r' || ch === '\t' ||
22632 ch === '\n' || ch === '\v' || ch === '\u00A0');
22635 isIdent: function(ch) {
22636 return ('a' <= ch && ch <= 'z' ||
22637 'A' <= ch && ch <= 'Z' ||
22638 '_' === ch || ch === '$');
22641 isExpOperator: function(ch) {
22642 return (ch === '-' || ch === '+' || this.isNumber(ch));
22645 throwError: function(error, start, end) {
22646 end = end || this.index;
22647 var colStr = (isDefined(start)
22648 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
22650 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
22651 error, colStr, this.text);
22654 readNumber: function() {
22656 var start = this.index;
22657 while (this.index < this.text.length) {
22658 var ch = lowercase(this.text.charAt(this.index));
22659 if (ch == '.' || this.isNumber(ch)) {
22662 var peekCh = this.peek();
22663 if (ch == 'e' && this.isExpOperator(peekCh)) {
22665 } else if (this.isExpOperator(ch) &&
22666 peekCh && this.isNumber(peekCh) &&
22667 number.charAt(number.length - 1) == 'e') {
22669 } else if (this.isExpOperator(ch) &&
22670 (!peekCh || !this.isNumber(peekCh)) &&
22671 number.charAt(number.length - 1) == 'e') {
22672 this.throwError('Invalid exponent');
22683 value: Number(number)
22687 readIdent: function() {
22688 var start = this.index;
22689 while (this.index < this.text.length) {
22690 var ch = this.text.charAt(this.index);
22691 if (!(this.isIdent(ch) || this.isNumber(ch))) {
22698 text: this.text.slice(start, this.index),
22703 readString: function(quote) {
22704 var start = this.index;
22707 var rawString = quote;
22708 var escape = false;
22709 while (this.index < this.text.length) {
22710 var ch = this.text.charAt(this.index);
22714 var hex = this.text.substring(this.index + 1, this.index + 5);
22715 if (!hex.match(/[\da-f]{4}/i)) {
22716 this.throwError('Invalid unicode escape [\\u' + hex + ']');
22719 string += String.fromCharCode(parseInt(hex, 16));
22721 var rep = ESCAPE[ch];
22722 string = string + (rep || ch);
22725 } else if (ch === '\\') {
22727 } else if (ch === quote) {
22741 this.throwError('Unterminated quote', start);
22745 var AST = function(lexer, options) {
22746 this.lexer = lexer;
22747 this.options = options;
22750 AST.Program = 'Program';
22751 AST.ExpressionStatement = 'ExpressionStatement';
22752 AST.AssignmentExpression = 'AssignmentExpression';
22753 AST.ConditionalExpression = 'ConditionalExpression';
22754 AST.LogicalExpression = 'LogicalExpression';
22755 AST.BinaryExpression = 'BinaryExpression';
22756 AST.UnaryExpression = 'UnaryExpression';
22757 AST.CallExpression = 'CallExpression';
22758 AST.MemberExpression = 'MemberExpression';
22759 AST.Identifier = 'Identifier';
22760 AST.Literal = 'Literal';
22761 AST.ArrayExpression = 'ArrayExpression';
22762 AST.Property = 'Property';
22763 AST.ObjectExpression = 'ObjectExpression';
22764 AST.ThisExpression = 'ThisExpression';
22765 AST.LocalsExpression = 'LocalsExpression';
22767 // Internal use only
22768 AST.NGValueParameter = 'NGValueParameter';
22771 ast: function(text) {
22773 this.tokens = this.lexer.lex(text);
22775 var value = this.program();
22777 if (this.tokens.length !== 0) {
22778 this.throwError('is an unexpected token', this.tokens[0]);
22784 program: function() {
22787 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
22788 body.push(this.expressionStatement());
22789 if (!this.expect(';')) {
22790 return { type: AST.Program, body: body};
22795 expressionStatement: function() {
22796 return { type: AST.ExpressionStatement, expression: this.filterChain() };
22799 filterChain: function() {
22800 var left = this.expression();
22802 while ((token = this.expect('|'))) {
22803 left = this.filter(left);
22808 expression: function() {
22809 return this.assignment();
22812 assignment: function() {
22813 var result = this.ternary();
22814 if (this.expect('=')) {
22815 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
22820 ternary: function() {
22821 var test = this.logicalOR();
22824 if (this.expect('?')) {
22825 alternate = this.expression();
22826 if (this.consume(':')) {
22827 consequent = this.expression();
22828 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
22834 logicalOR: function() {
22835 var left = this.logicalAND();
22836 while (this.expect('||')) {
22837 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
22842 logicalAND: function() {
22843 var left = this.equality();
22844 while (this.expect('&&')) {
22845 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
22850 equality: function() {
22851 var left = this.relational();
22853 while ((token = this.expect('==','!=','===','!=='))) {
22854 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
22859 relational: function() {
22860 var left = this.additive();
22862 while ((token = this.expect('<', '>', '<=', '>='))) {
22863 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
22868 additive: function() {
22869 var left = this.multiplicative();
22871 while ((token = this.expect('+','-'))) {
22872 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
22877 multiplicative: function() {
22878 var left = this.unary();
22880 while ((token = this.expect('*','/','%'))) {
22881 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
22886 unary: function() {
22888 if ((token = this.expect('+', '-', '!'))) {
22889 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
22891 return this.primary();
22895 primary: function() {
22897 if (this.expect('(')) {
22898 primary = this.filterChain();
22900 } else if (this.expect('[')) {
22901 primary = this.arrayDeclaration();
22902 } else if (this.expect('{')) {
22903 primary = this.object();
22904 } else if (this.constants.hasOwnProperty(this.peek().text)) {
22905 primary = copy(this.constants[this.consume().text]);
22906 } else if (this.peek().identifier) {
22907 primary = this.identifier();
22908 } else if (this.peek().constant) {
22909 primary = this.constant();
22911 this.throwError('not a primary expression', this.peek());
22915 while ((next = this.expect('(', '[', '.'))) {
22916 if (next.text === '(') {
22917 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
22919 } else if (next.text === '[') {
22920 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
22922 } else if (next.text === '.') {
22923 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
22925 this.throwError('IMPOSSIBLE');
22931 filter: function(baseExpression) {
22932 var args = [baseExpression];
22933 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
22935 while (this.expect(':')) {
22936 args.push(this.expression());
22942 parseArguments: function() {
22944 if (this.peekToken().text !== ')') {
22946 args.push(this.expression());
22947 } while (this.expect(','));
22952 identifier: function() {
22953 var token = this.consume();
22954 if (!token.identifier) {
22955 this.throwError('is not a valid identifier', token);
22957 return { type: AST.Identifier, name: token.text };
22960 constant: function() {
22961 // TODO check that it is a constant
22962 return { type: AST.Literal, value: this.consume().value };
22965 arrayDeclaration: function() {
22967 if (this.peekToken().text !== ']') {
22969 if (this.peek(']')) {
22970 // Support trailing commas per ES5.1.
22973 elements.push(this.expression());
22974 } while (this.expect(','));
22978 return { type: AST.ArrayExpression, elements: elements };
22981 object: function() {
22982 var properties = [], property;
22983 if (this.peekToken().text !== '}') {
22985 if (this.peek('}')) {
22986 // Support trailing commas per ES5.1.
22989 property = {type: AST.Property, kind: 'init'};
22990 if (this.peek().constant) {
22991 property.key = this.constant();
22992 } else if (this.peek().identifier) {
22993 property.key = this.identifier();
22995 this.throwError("invalid key", this.peek());
22998 property.value = this.expression();
22999 properties.push(property);
23000 } while (this.expect(','));
23004 return {type: AST.ObjectExpression, properties: properties };
23007 throwError: function(msg, token) {
23008 throw $parseMinErr('syntax',
23009 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
23010 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
23013 consume: function(e1) {
23014 if (this.tokens.length === 0) {
23015 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
23018 var token = this.expect(e1);
23020 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
23025 peekToken: function() {
23026 if (this.tokens.length === 0) {
23027 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
23029 return this.tokens[0];
23032 peek: function(e1, e2, e3, e4) {
23033 return this.peekAhead(0, e1, e2, e3, e4);
23036 peekAhead: function(i, e1, e2, e3, e4) {
23037 if (this.tokens.length > i) {
23038 var token = this.tokens[i];
23039 var t = token.text;
23040 if (t === e1 || t === e2 || t === e3 || t === e4 ||
23041 (!e1 && !e2 && !e3 && !e4)) {
23048 expect: function(e1, e2, e3, e4) {
23049 var token = this.peek(e1, e2, e3, e4);
23051 this.tokens.shift();
23058 /* `undefined` is not a constant, it is an identifier,
23059 * but using it as an identifier is not supported
23062 'true': { type: AST.Literal, value: true },
23063 'false': { type: AST.Literal, value: false },
23064 'null': { type: AST.Literal, value: null },
23065 'undefined': {type: AST.Literal, value: undefined },
23066 'this': {type: AST.ThisExpression },
23067 '$locals': {type: AST.LocalsExpression }
23071 function ifDefined(v, d) {
23072 return typeof v !== 'undefined' ? v : d;
23075 function plusFn(l, r) {
23076 if (typeof l === 'undefined') return r;
23077 if (typeof r === 'undefined') return l;
23081 function isStateless($filter, filterName) {
23082 var fn = $filter(filterName);
23083 return !fn.$stateful;
23086 function findConstantAndWatchExpressions(ast, $filter) {
23089 switch (ast.type) {
23091 allConstants = true;
23092 forEach(ast.body, function(expr) {
23093 findConstantAndWatchExpressions(expr.expression, $filter);
23094 allConstants = allConstants && expr.expression.constant;
23096 ast.constant = allConstants;
23099 ast.constant = true;
23102 case AST.UnaryExpression:
23103 findConstantAndWatchExpressions(ast.argument, $filter);
23104 ast.constant = ast.argument.constant;
23105 ast.toWatch = ast.argument.toWatch;
23107 case AST.BinaryExpression:
23108 findConstantAndWatchExpressions(ast.left, $filter);
23109 findConstantAndWatchExpressions(ast.right, $filter);
23110 ast.constant = ast.left.constant && ast.right.constant;
23111 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
23113 case AST.LogicalExpression:
23114 findConstantAndWatchExpressions(ast.left, $filter);
23115 findConstantAndWatchExpressions(ast.right, $filter);
23116 ast.constant = ast.left.constant && ast.right.constant;
23117 ast.toWatch = ast.constant ? [] : [ast];
23119 case AST.ConditionalExpression:
23120 findConstantAndWatchExpressions(ast.test, $filter);
23121 findConstantAndWatchExpressions(ast.alternate, $filter);
23122 findConstantAndWatchExpressions(ast.consequent, $filter);
23123 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
23124 ast.toWatch = ast.constant ? [] : [ast];
23126 case AST.Identifier:
23127 ast.constant = false;
23128 ast.toWatch = [ast];
23130 case AST.MemberExpression:
23131 findConstantAndWatchExpressions(ast.object, $filter);
23132 if (ast.computed) {
23133 findConstantAndWatchExpressions(ast.property, $filter);
23135 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
23136 ast.toWatch = [ast];
23138 case AST.CallExpression:
23139 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
23141 forEach(ast.arguments, function(expr) {
23142 findConstantAndWatchExpressions(expr, $filter);
23143 allConstants = allConstants && expr.constant;
23144 if (!expr.constant) {
23145 argsToWatch.push.apply(argsToWatch, expr.toWatch);
23148 ast.constant = allConstants;
23149 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
23151 case AST.AssignmentExpression:
23152 findConstantAndWatchExpressions(ast.left, $filter);
23153 findConstantAndWatchExpressions(ast.right, $filter);
23154 ast.constant = ast.left.constant && ast.right.constant;
23155 ast.toWatch = [ast];
23157 case AST.ArrayExpression:
23158 allConstants = true;
23160 forEach(ast.elements, function(expr) {
23161 findConstantAndWatchExpressions(expr, $filter);
23162 allConstants = allConstants && expr.constant;
23163 if (!expr.constant) {
23164 argsToWatch.push.apply(argsToWatch, expr.toWatch);
23167 ast.constant = allConstants;
23168 ast.toWatch = argsToWatch;
23170 case AST.ObjectExpression:
23171 allConstants = true;
23173 forEach(ast.properties, function(property) {
23174 findConstantAndWatchExpressions(property.value, $filter);
23175 allConstants = allConstants && property.value.constant;
23176 if (!property.value.constant) {
23177 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
23180 ast.constant = allConstants;
23181 ast.toWatch = argsToWatch;
23183 case AST.ThisExpression:
23184 ast.constant = false;
23187 case AST.LocalsExpression:
23188 ast.constant = false;
23194 function getInputs(body) {
23195 if (body.length != 1) return;
23196 var lastExpression = body[0].expression;
23197 var candidate = lastExpression.toWatch;
23198 if (candidate.length !== 1) return candidate;
23199 return candidate[0] !== lastExpression ? candidate : undefined;
23202 function isAssignable(ast) {
23203 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
23206 function assignableAST(ast) {
23207 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
23208 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
23212 function isLiteral(ast) {
23213 return ast.body.length === 0 ||
23214 ast.body.length === 1 && (
23215 ast.body[0].expression.type === AST.Literal ||
23216 ast.body[0].expression.type === AST.ArrayExpression ||
23217 ast.body[0].expression.type === AST.ObjectExpression);
23220 function isConstant(ast) {
23221 return ast.constant;
23224 function ASTCompiler(astBuilder, $filter) {
23225 this.astBuilder = astBuilder;
23226 this.$filter = $filter;
23229 ASTCompiler.prototype = {
23230 compile: function(expression, expensiveChecks) {
23232 var ast = this.astBuilder.ast(expression);
23236 expensiveChecks: expensiveChecks,
23237 fn: {vars: [], body: [], own: {}},
23238 assign: {vars: [], body: [], own: {}},
23241 findConstantAndWatchExpressions(ast, self.$filter);
23244 this.stage = 'assign';
23245 if ((assignable = assignableAST(ast))) {
23246 this.state.computing = 'assign';
23247 var result = this.nextId();
23248 this.recurse(assignable, result);
23249 this.return_(result);
23250 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
23252 var toWatch = getInputs(ast.body);
23253 self.stage = 'inputs';
23254 forEach(toWatch, function(watch, key) {
23255 var fnKey = 'fn' + key;
23256 self.state[fnKey] = {vars: [], body: [], own: {}};
23257 self.state.computing = fnKey;
23258 var intoId = self.nextId();
23259 self.recurse(watch, intoId);
23260 self.return_(intoId);
23261 self.state.inputs.push(fnKey);
23262 watch.watchId = key;
23264 this.state.computing = 'fn';
23265 this.stage = 'main';
23268 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
23269 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
23270 '"' + this.USE + ' ' + this.STRICT + '";\n' +
23271 this.filterPrefix() +
23272 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
23278 var fn = (new Function('$filter',
23279 'ensureSafeMemberName',
23280 'ensureSafeObject',
23281 'ensureSafeFunction',
23283 'ensureSafeAssignContext',
23289 ensureSafeMemberName,
23291 ensureSafeFunction,
23293 ensureSafeAssignContext,
23298 this.state = this.stage = undefined;
23299 fn.literal = isLiteral(ast);
23300 fn.constant = isConstant(ast);
23308 watchFns: function() {
23310 var fns = this.state.inputs;
23312 forEach(fns, function(name) {
23313 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
23316 result.push('fn.inputs=[' + fns.join(',') + '];');
23318 return result.join('');
23321 generateFunction: function(name, params) {
23322 return 'function(' + params + '){' +
23323 this.varsPrefix(name) +
23328 filterPrefix: function() {
23331 forEach(this.state.filters, function(id, filter) {
23332 parts.push(id + '=$filter(' + self.escape(filter) + ')');
23334 if (parts.length) return 'var ' + parts.join(',') + ';';
23338 varsPrefix: function(section) {
23339 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
23342 body: function(section) {
23343 return this.state[section].body.join('');
23346 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
23347 var left, right, self = this, args, expression;
23348 recursionFn = recursionFn || noop;
23349 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
23350 intoId = intoId || this.nextId();
23352 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
23353 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
23357 switch (ast.type) {
23359 forEach(ast.body, function(expression, pos) {
23360 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
23361 if (pos !== ast.body.length - 1) {
23362 self.current().body.push(right, ';');
23364 self.return_(right);
23369 expression = this.escape(ast.value);
23370 this.assign(intoId, expression);
23371 recursionFn(expression);
23373 case AST.UnaryExpression:
23374 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
23375 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
23376 this.assign(intoId, expression);
23377 recursionFn(expression);
23379 case AST.BinaryExpression:
23380 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
23381 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
23382 if (ast.operator === '+') {
23383 expression = this.plus(left, right);
23384 } else if (ast.operator === '-') {
23385 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
23387 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
23389 this.assign(intoId, expression);
23390 recursionFn(expression);
23392 case AST.LogicalExpression:
23393 intoId = intoId || this.nextId();
23394 self.recurse(ast.left, intoId);
23395 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
23396 recursionFn(intoId);
23398 case AST.ConditionalExpression:
23399 intoId = intoId || this.nextId();
23400 self.recurse(ast.test, intoId);
23401 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
23402 recursionFn(intoId);
23404 case AST.Identifier:
23405 intoId = intoId || this.nextId();
23407 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
23408 nameId.computed = false;
23409 nameId.name = ast.name;
23411 ensureSafeMemberName(ast.name);
23412 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
23414 self.if_(self.stage === 'inputs' || 's', function() {
23415 if (create && create !== 1) {
23417 self.not(self.nonComputedMember('s', ast.name)),
23418 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
23420 self.assign(intoId, self.nonComputedMember('s', ast.name));
23422 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
23424 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
23425 self.addEnsureSafeObject(intoId);
23427 recursionFn(intoId);
23429 case AST.MemberExpression:
23430 left = nameId && (nameId.context = this.nextId()) || this.nextId();
23431 intoId = intoId || this.nextId();
23432 self.recurse(ast.object, left, undefined, function() {
23433 self.if_(self.notNull(left), function() {
23434 if (create && create !== 1) {
23435 self.addEnsureSafeAssignContext(left);
23437 if (ast.computed) {
23438 right = self.nextId();
23439 self.recurse(ast.property, right);
23440 self.getStringValue(right);
23441 self.addEnsureSafeMemberName(right);
23442 if (create && create !== 1) {
23443 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
23445 expression = self.ensureSafeObject(self.computedMember(left, right));
23446 self.assign(intoId, expression);
23448 nameId.computed = true;
23449 nameId.name = right;
23452 ensureSafeMemberName(ast.property.name);
23453 if (create && create !== 1) {
23454 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
23456 expression = self.nonComputedMember(left, ast.property.name);
23457 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
23458 expression = self.ensureSafeObject(expression);
23460 self.assign(intoId, expression);
23462 nameId.computed = false;
23463 nameId.name = ast.property.name;
23467 self.assign(intoId, 'undefined');
23469 recursionFn(intoId);
23472 case AST.CallExpression:
23473 intoId = intoId || this.nextId();
23475 right = self.filter(ast.callee.name);
23477 forEach(ast.arguments, function(expr) {
23478 var argument = self.nextId();
23479 self.recurse(expr, argument);
23480 args.push(argument);
23482 expression = right + '(' + args.join(',') + ')';
23483 self.assign(intoId, expression);
23484 recursionFn(intoId);
23486 right = self.nextId();
23489 self.recurse(ast.callee, right, left, function() {
23490 self.if_(self.notNull(right), function() {
23491 self.addEnsureSafeFunction(right);
23492 forEach(ast.arguments, function(expr) {
23493 self.recurse(expr, self.nextId(), undefined, function(argument) {
23494 args.push(self.ensureSafeObject(argument));
23498 if (!self.state.expensiveChecks) {
23499 self.addEnsureSafeObject(left.context);
23501 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
23503 expression = right + '(' + args.join(',') + ')';
23505 expression = self.ensureSafeObject(expression);
23506 self.assign(intoId, expression);
23508 self.assign(intoId, 'undefined');
23510 recursionFn(intoId);
23514 case AST.AssignmentExpression:
23515 right = this.nextId();
23517 if (!isAssignable(ast.left)) {
23518 throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
23520 this.recurse(ast.left, undefined, left, function() {
23521 self.if_(self.notNull(left.context), function() {
23522 self.recurse(ast.right, right);
23523 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
23524 self.addEnsureSafeAssignContext(left.context);
23525 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
23526 self.assign(intoId, expression);
23527 recursionFn(intoId || expression);
23531 case AST.ArrayExpression:
23533 forEach(ast.elements, function(expr) {
23534 self.recurse(expr, self.nextId(), undefined, function(argument) {
23535 args.push(argument);
23538 expression = '[' + args.join(',') + ']';
23539 this.assign(intoId, expression);
23540 recursionFn(expression);
23542 case AST.ObjectExpression:
23544 forEach(ast.properties, function(property) {
23545 self.recurse(property.value, self.nextId(), undefined, function(expr) {
23546 args.push(self.escape(
23547 property.key.type === AST.Identifier ? property.key.name :
23548 ('' + property.key.value)) +
23552 expression = '{' + args.join(',') + '}';
23553 this.assign(intoId, expression);
23554 recursionFn(expression);
23556 case AST.ThisExpression:
23557 this.assign(intoId, 's');
23560 case AST.LocalsExpression:
23561 this.assign(intoId, 'l');
23564 case AST.NGValueParameter:
23565 this.assign(intoId, 'v');
23571 getHasOwnProperty: function(element, property) {
23572 var key = element + '.' + property;
23573 var own = this.current().own;
23574 if (!own.hasOwnProperty(key)) {
23575 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
23580 assign: function(id, value) {
23582 this.current().body.push(id, '=', value, ';');
23586 filter: function(filterName) {
23587 if (!this.state.filters.hasOwnProperty(filterName)) {
23588 this.state.filters[filterName] = this.nextId(true);
23590 return this.state.filters[filterName];
23593 ifDefined: function(id, defaultValue) {
23594 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
23597 plus: function(left, right) {
23598 return 'plus(' + left + ',' + right + ')';
23601 return_: function(id) {
23602 this.current().body.push('return ', id, ';');
23605 if_: function(test, alternate, consequent) {
23606 if (test === true) {
23609 var body = this.current().body;
23610 body.push('if(', test, '){');
23614 body.push('else{');
23621 not: function(expression) {
23622 return '!(' + expression + ')';
23625 notNull: function(expression) {
23626 return expression + '!=null';
23629 nonComputedMember: function(left, right) {
23630 return left + '.' + right;
23633 computedMember: function(left, right) {
23634 return left + '[' + right + ']';
23637 member: function(left, right, computed) {
23638 if (computed) return this.computedMember(left, right);
23639 return this.nonComputedMember(left, right);
23642 addEnsureSafeObject: function(item) {
23643 this.current().body.push(this.ensureSafeObject(item), ';');
23646 addEnsureSafeMemberName: function(item) {
23647 this.current().body.push(this.ensureSafeMemberName(item), ';');
23650 addEnsureSafeFunction: function(item) {
23651 this.current().body.push(this.ensureSafeFunction(item), ';');
23654 addEnsureSafeAssignContext: function(item) {
23655 this.current().body.push(this.ensureSafeAssignContext(item), ';');
23658 ensureSafeObject: function(item) {
23659 return 'ensureSafeObject(' + item + ',text)';
23662 ensureSafeMemberName: function(item) {
23663 return 'ensureSafeMemberName(' + item + ',text)';
23666 ensureSafeFunction: function(item) {
23667 return 'ensureSafeFunction(' + item + ',text)';
23670 getStringValue: function(item) {
23671 this.assign(item, 'getStringValue(' + item + ')');
23674 ensureSafeAssignContext: function(item) {
23675 return 'ensureSafeAssignContext(' + item + ',text)';
23678 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
23680 return function() {
23681 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
23685 lazyAssign: function(id, value) {
23687 return function() {
23688 self.assign(id, value);
23692 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
23694 stringEscapeFn: function(c) {
23695 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
23698 escape: function(value) {
23699 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
23700 if (isNumber(value)) return value.toString();
23701 if (value === true) return 'true';
23702 if (value === false) return 'false';
23703 if (value === null) return 'null';
23704 if (typeof value === 'undefined') return 'undefined';
23706 throw $parseMinErr('esc', 'IMPOSSIBLE');
23709 nextId: function(skip, init) {
23710 var id = 'v' + (this.state.nextId++);
23712 this.current().vars.push(id + (init ? '=' + init : ''));
23717 current: function() {
23718 return this.state[this.state.computing];
23723 function ASTInterpreter(astBuilder, $filter) {
23724 this.astBuilder = astBuilder;
23725 this.$filter = $filter;
23728 ASTInterpreter.prototype = {
23729 compile: function(expression, expensiveChecks) {
23731 var ast = this.astBuilder.ast(expression);
23732 this.expression = expression;
23733 this.expensiveChecks = expensiveChecks;
23734 findConstantAndWatchExpressions(ast, self.$filter);
23737 if ((assignable = assignableAST(ast))) {
23738 assign = this.recurse(assignable);
23740 var toWatch = getInputs(ast.body);
23744 forEach(toWatch, function(watch, key) {
23745 var input = self.recurse(watch);
23746 watch.input = input;
23747 inputs.push(input);
23748 watch.watchId = key;
23751 var expressions = [];
23752 forEach(ast.body, function(expression) {
23753 expressions.push(self.recurse(expression.expression));
23755 var fn = ast.body.length === 0 ? function() {} :
23756 ast.body.length === 1 ? expressions[0] :
23757 function(scope, locals) {
23759 forEach(expressions, function(exp) {
23760 lastValue = exp(scope, locals);
23765 fn.assign = function(scope, value, locals) {
23766 return assign(scope, locals, value);
23770 fn.inputs = inputs;
23772 fn.literal = isLiteral(ast);
23773 fn.constant = isConstant(ast);
23777 recurse: function(ast, context, create) {
23778 var left, right, self = this, args, expression;
23780 return this.inputs(ast.input, ast.watchId);
23782 switch (ast.type) {
23784 return this.value(ast.value, context);
23785 case AST.UnaryExpression:
23786 right = this.recurse(ast.argument);
23787 return this['unary' + ast.operator](right, context);
23788 case AST.BinaryExpression:
23789 left = this.recurse(ast.left);
23790 right = this.recurse(ast.right);
23791 return this['binary' + ast.operator](left, right, context);
23792 case AST.LogicalExpression:
23793 left = this.recurse(ast.left);
23794 right = this.recurse(ast.right);
23795 return this['binary' + ast.operator](left, right, context);
23796 case AST.ConditionalExpression:
23797 return this['ternary?:'](
23798 this.recurse(ast.test),
23799 this.recurse(ast.alternate),
23800 this.recurse(ast.consequent),
23803 case AST.Identifier:
23804 ensureSafeMemberName(ast.name, self.expression);
23805 return self.identifier(ast.name,
23806 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
23807 context, create, self.expression);
23808 case AST.MemberExpression:
23809 left = this.recurse(ast.object, false, !!create);
23810 if (!ast.computed) {
23811 ensureSafeMemberName(ast.property.name, self.expression);
23812 right = ast.property.name;
23814 if (ast.computed) right = this.recurse(ast.property);
23815 return ast.computed ?
23816 this.computedMember(left, right, context, create, self.expression) :
23817 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
23818 case AST.CallExpression:
23820 forEach(ast.arguments, function(expr) {
23821 args.push(self.recurse(expr));
23823 if (ast.filter) right = this.$filter(ast.callee.name);
23824 if (!ast.filter) right = this.recurse(ast.callee, true);
23825 return ast.filter ?
23826 function(scope, locals, assign, inputs) {
23828 for (var i = 0; i < args.length; ++i) {
23829 values.push(args[i](scope, locals, assign, inputs));
23831 var value = right.apply(undefined, values, inputs);
23832 return context ? {context: undefined, name: undefined, value: value} : value;
23834 function(scope, locals, assign, inputs) {
23835 var rhs = right(scope, locals, assign, inputs);
23837 if (rhs.value != null) {
23838 ensureSafeObject(rhs.context, self.expression);
23839 ensureSafeFunction(rhs.value, self.expression);
23841 for (var i = 0; i < args.length; ++i) {
23842 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
23844 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
23846 return context ? {value: value} : value;
23848 case AST.AssignmentExpression:
23849 left = this.recurse(ast.left, true, 1);
23850 right = this.recurse(ast.right);
23851 return function(scope, locals, assign, inputs) {
23852 var lhs = left(scope, locals, assign, inputs);
23853 var rhs = right(scope, locals, assign, inputs);
23854 ensureSafeObject(lhs.value, self.expression);
23855 ensureSafeAssignContext(lhs.context);
23856 lhs.context[lhs.name] = rhs;
23857 return context ? {value: rhs} : rhs;
23859 case AST.ArrayExpression:
23861 forEach(ast.elements, function(expr) {
23862 args.push(self.recurse(expr));
23864 return function(scope, locals, assign, inputs) {
23866 for (var i = 0; i < args.length; ++i) {
23867 value.push(args[i](scope, locals, assign, inputs));
23869 return context ? {value: value} : value;
23871 case AST.ObjectExpression:
23873 forEach(ast.properties, function(property) {
23874 args.push({key: property.key.type === AST.Identifier ?
23875 property.key.name :
23876 ('' + property.key.value),
23877 value: self.recurse(property.value)
23880 return function(scope, locals, assign, inputs) {
23882 for (var i = 0; i < args.length; ++i) {
23883 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
23885 return context ? {value: value} : value;
23887 case AST.ThisExpression:
23888 return function(scope) {
23889 return context ? {value: scope} : scope;
23891 case AST.LocalsExpression:
23892 return function(scope, locals) {
23893 return context ? {value: locals} : locals;
23895 case AST.NGValueParameter:
23896 return function(scope, locals, assign, inputs) {
23897 return context ? {value: assign} : assign;
23902 'unary+': function(argument, context) {
23903 return function(scope, locals, assign, inputs) {
23904 var arg = argument(scope, locals, assign, inputs);
23905 if (isDefined(arg)) {
23910 return context ? {value: arg} : arg;
23913 'unary-': function(argument, context) {
23914 return function(scope, locals, assign, inputs) {
23915 var arg = argument(scope, locals, assign, inputs);
23916 if (isDefined(arg)) {
23921 return context ? {value: arg} : arg;
23924 'unary!': function(argument, context) {
23925 return function(scope, locals, assign, inputs) {
23926 var arg = !argument(scope, locals, assign, inputs);
23927 return context ? {value: arg} : arg;
23930 'binary+': function(left, right, context) {
23931 return function(scope, locals, assign, inputs) {
23932 var lhs = left(scope, locals, assign, inputs);
23933 var rhs = right(scope, locals, assign, inputs);
23934 var arg = plusFn(lhs, rhs);
23935 return context ? {value: arg} : arg;
23938 'binary-': function(left, right, context) {
23939 return function(scope, locals, assign, inputs) {
23940 var lhs = left(scope, locals, assign, inputs);
23941 var rhs = right(scope, locals, assign, inputs);
23942 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
23943 return context ? {value: arg} : arg;
23946 'binary*': function(left, right, context) {
23947 return function(scope, locals, assign, inputs) {
23948 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
23949 return context ? {value: arg} : arg;
23952 'binary/': function(left, right, context) {
23953 return function(scope, locals, assign, inputs) {
23954 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
23955 return context ? {value: arg} : arg;
23958 'binary%': function(left, right, context) {
23959 return function(scope, locals, assign, inputs) {
23960 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
23961 return context ? {value: arg} : arg;
23964 'binary===': function(left, right, context) {
23965 return function(scope, locals, assign, inputs) {
23966 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
23967 return context ? {value: arg} : arg;
23970 'binary!==': function(left, right, context) {
23971 return function(scope, locals, assign, inputs) {
23972 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
23973 return context ? {value: arg} : arg;
23976 'binary==': function(left, right, context) {
23977 return function(scope, locals, assign, inputs) {
23978 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
23979 return context ? {value: arg} : arg;
23982 'binary!=': function(left, right, context) {
23983 return function(scope, locals, assign, inputs) {
23984 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
23985 return context ? {value: arg} : arg;
23988 'binary<': function(left, right, context) {
23989 return function(scope, locals, assign, inputs) {
23990 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
23991 return context ? {value: arg} : arg;
23994 'binary>': function(left, right, context) {
23995 return function(scope, locals, assign, inputs) {
23996 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
23997 return context ? {value: arg} : arg;
24000 'binary<=': function(left, right, context) {
24001 return function(scope, locals, assign, inputs) {
24002 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
24003 return context ? {value: arg} : arg;
24006 'binary>=': function(left, right, context) {
24007 return function(scope, locals, assign, inputs) {
24008 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
24009 return context ? {value: arg} : arg;
24012 'binary&&': function(left, right, context) {
24013 return function(scope, locals, assign, inputs) {
24014 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
24015 return context ? {value: arg} : arg;
24018 'binary||': function(left, right, context) {
24019 return function(scope, locals, assign, inputs) {
24020 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
24021 return context ? {value: arg} : arg;
24024 'ternary?:': function(test, alternate, consequent, context) {
24025 return function(scope, locals, assign, inputs) {
24026 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
24027 return context ? {value: arg} : arg;
24030 value: function(value, context) {
24031 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
24033 identifier: function(name, expensiveChecks, context, create, expression) {
24034 return function(scope, locals, assign, inputs) {
24035 var base = locals && (name in locals) ? locals : scope;
24036 if (create && create !== 1 && base && !(base[name])) {
24039 var value = base ? base[name] : undefined;
24040 if (expensiveChecks) {
24041 ensureSafeObject(value, expression);
24044 return {context: base, name: name, value: value};
24050 computedMember: function(left, right, context, create, expression) {
24051 return function(scope, locals, assign, inputs) {
24052 var lhs = left(scope, locals, assign, inputs);
24056 rhs = right(scope, locals, assign, inputs);
24057 rhs = getStringValue(rhs);
24058 ensureSafeMemberName(rhs, expression);
24059 if (create && create !== 1) {
24060 ensureSafeAssignContext(lhs);
24061 if (lhs && !(lhs[rhs])) {
24066 ensureSafeObject(value, expression);
24069 return {context: lhs, name: rhs, value: value};
24075 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
24076 return function(scope, locals, assign, inputs) {
24077 var lhs = left(scope, locals, assign, inputs);
24078 if (create && create !== 1) {
24079 ensureSafeAssignContext(lhs);
24080 if (lhs && !(lhs[right])) {
24084 var value = lhs != null ? lhs[right] : undefined;
24085 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
24086 ensureSafeObject(value, expression);
24089 return {context: lhs, name: right, value: value};
24095 inputs: function(input, watchId) {
24096 return function(scope, value, locals, inputs) {
24097 if (inputs) return inputs[watchId];
24098 return input(scope, value, locals);
24106 var Parser = function(lexer, $filter, options) {
24107 this.lexer = lexer;
24108 this.$filter = $filter;
24109 this.options = options;
24110 this.ast = new AST(this.lexer);
24111 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
24112 new ASTCompiler(this.ast, $filter);
24115 Parser.prototype = {
24116 constructor: Parser,
24118 parse: function(text) {
24119 return this.astCompiler.compile(text, this.options.expensiveChecks);
24123 function isPossiblyDangerousMemberName(name) {
24124 return name == 'constructor';
24127 var objectValueOf = Object.prototype.valueOf;
24129 function getValueOf(value) {
24130 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
24133 ///////////////////////////////////
24142 * Converts Angular {@link guide/expression expression} into a function.
24145 * var getter = $parse('user.name');
24146 * var setter = getter.assign;
24147 * var context = {user:{name:'angular'}};
24148 * var locals = {user:{name:'local'}};
24150 * expect(getter(context)).toEqual('angular');
24151 * setter(context, 'newValue');
24152 * expect(context.user.name).toEqual('newValue');
24153 * expect(getter(context, locals)).toEqual('local');
24157 * @param {string} expression String expression to compile.
24158 * @returns {function(context, locals)} a function which represents the compiled expression:
24160 * * `context` – `{object}` – an object against which any expressions embedded in the strings
24161 * are evaluated against (typically a scope object).
24162 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
24165 * The returned function also has the following properties:
24166 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
24168 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
24169 * constant literals.
24170 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
24171 * set to a function to change its value on the given context.
24178 * @name $parseProvider
24181 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
24184 function $ParseProvider() {
24185 var cacheDefault = createMap();
24186 var cacheExpensive = createMap();
24188 this.$get = ['$filter', function($filter) {
24189 var noUnsafeEval = csp().noUnsafeEval;
24190 var $parseOptions = {
24192 expensiveChecks: false
24194 $parseOptionsExpensive = {
24196 expensiveChecks: true
24198 var runningChecksEnabled = false;
24200 $parse.$$runningExpensiveChecks = function() {
24201 return runningChecksEnabled;
24206 function $parse(exp, interceptorFn, expensiveChecks) {
24207 var parsedExpression, oneTime, cacheKey;
24209 expensiveChecks = expensiveChecks || runningChecksEnabled;
24211 switch (typeof exp) {
24216 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
24217 parsedExpression = cache[cacheKey];
24219 if (!parsedExpression) {
24220 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
24222 exp = exp.substring(2);
24224 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
24225 var lexer = new Lexer(parseOptions);
24226 var parser = new Parser(lexer, $filter, parseOptions);
24227 parsedExpression = parser.parse(exp);
24228 if (parsedExpression.constant) {
24229 parsedExpression.$$watchDelegate = constantWatchDelegate;
24230 } else if (oneTime) {
24231 parsedExpression.$$watchDelegate = parsedExpression.literal ?
24232 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
24233 } else if (parsedExpression.inputs) {
24234 parsedExpression.$$watchDelegate = inputsWatchDelegate;
24236 if (expensiveChecks) {
24237 parsedExpression = expensiveChecksInterceptor(parsedExpression);
24239 cache[cacheKey] = parsedExpression;
24241 return addInterceptor(parsedExpression, interceptorFn);
24244 return addInterceptor(exp, interceptorFn);
24247 return addInterceptor(noop, interceptorFn);
24251 function expensiveChecksInterceptor(fn) {
24252 if (!fn) return fn;
24253 expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
24254 expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
24255 expensiveCheckFn.constant = fn.constant;
24256 expensiveCheckFn.literal = fn.literal;
24257 for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
24258 fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
24260 expensiveCheckFn.inputs = fn.inputs;
24262 return expensiveCheckFn;
24264 function expensiveCheckFn(scope, locals, assign, inputs) {
24265 var expensiveCheckOldValue = runningChecksEnabled;
24266 runningChecksEnabled = true;
24268 return fn(scope, locals, assign, inputs);
24270 runningChecksEnabled = expensiveCheckOldValue;
24275 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
24277 if (newValue == null || oldValueOfValue == null) { // null/undefined
24278 return newValue === oldValueOfValue;
24281 if (typeof newValue === 'object') {
24283 // attempt to convert the value to a primitive type
24284 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
24285 // be cheaply dirty-checked
24286 newValue = getValueOf(newValue);
24288 if (typeof newValue === 'object') {
24289 // objects/arrays are not supported - deep-watching them would be too expensive
24293 // fall-through to the primitive equality check
24297 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
24300 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
24301 var inputExpressions = parsedExpression.inputs;
24304 if (inputExpressions.length === 1) {
24305 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
24306 inputExpressions = inputExpressions[0];
24307 return scope.$watch(function expressionInputWatch(scope) {
24308 var newInputValue = inputExpressions(scope);
24309 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
24310 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
24311 oldInputValueOf = newInputValue && getValueOf(newInputValue);
24314 }, listener, objectEquality, prettyPrintExpression);
24317 var oldInputValueOfValues = [];
24318 var oldInputValues = [];
24319 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
24320 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
24321 oldInputValues[i] = null;
24324 return scope.$watch(function expressionInputsWatch(scope) {
24325 var changed = false;
24327 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
24328 var newInputValue = inputExpressions[i](scope);
24329 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
24330 oldInputValues[i] = newInputValue;
24331 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
24336 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
24340 }, listener, objectEquality, prettyPrintExpression);
24343 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
24344 var unwatch, lastValue;
24345 return unwatch = scope.$watch(function oneTimeWatch(scope) {
24346 return parsedExpression(scope);
24347 }, function oneTimeListener(value, old, scope) {
24349 if (isFunction(listener)) {
24350 listener.apply(this, arguments);
24352 if (isDefined(value)) {
24353 scope.$$postDigest(function() {
24354 if (isDefined(lastValue)) {
24359 }, objectEquality);
24362 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
24363 var unwatch, lastValue;
24364 return unwatch = scope.$watch(function oneTimeWatch(scope) {
24365 return parsedExpression(scope);
24366 }, function oneTimeListener(value, old, scope) {
24368 if (isFunction(listener)) {
24369 listener.call(this, value, old, scope);
24371 if (isAllDefined(value)) {
24372 scope.$$postDigest(function() {
24373 if (isAllDefined(lastValue)) unwatch();
24376 }, objectEquality);
24378 function isAllDefined(value) {
24379 var allDefined = true;
24380 forEach(value, function(val) {
24381 if (!isDefined(val)) allDefined = false;
24387 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
24389 return unwatch = scope.$watch(function constantWatch(scope) {
24391 return parsedExpression(scope);
24392 }, listener, objectEquality);
24395 function addInterceptor(parsedExpression, interceptorFn) {
24396 if (!interceptorFn) return parsedExpression;
24397 var watchDelegate = parsedExpression.$$watchDelegate;
24398 var useInputs = false;
24401 watchDelegate !== oneTimeLiteralWatchDelegate &&
24402 watchDelegate !== oneTimeWatchDelegate;
24404 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
24405 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
24406 return interceptorFn(value, scope, locals);
24407 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
24408 var value = parsedExpression(scope, locals, assign, inputs);
24409 var result = interceptorFn(value, scope, locals);
24410 // we only return the interceptor's result if the
24411 // initial value is defined (for bind-once)
24412 return isDefined(value) ? result : value;
24415 // Propagate $$watchDelegates other then inputsWatchDelegate
24416 if (parsedExpression.$$watchDelegate &&
24417 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
24418 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
24419 } else if (!interceptorFn.$stateful) {
24420 // If there is an interceptor, but no watchDelegate then treat the interceptor like
24421 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
24422 fn.$$watchDelegate = inputsWatchDelegate;
24423 useInputs = !parsedExpression.inputs;
24424 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
24435 * @requires $rootScope
24438 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
24439 * when they are done processing.
24441 * This is an implementation of promises/deferred objects inspired by
24442 * [Kris Kowal's Q](https://github.com/kriskowal/q).
24444 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
24445 * implementations, and the other which resembles ES6 promises to some degree.
24449 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
24450 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
24451 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
24453 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
24456 * It can be used like so:
24459 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
24460 * // are available in the current lexical scope (they could have been injected or passed in).
24462 * function asyncGreet(name) {
24463 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
24464 * return $q(function(resolve, reject) {
24465 * setTimeout(function() {
24466 * if (okToGreet(name)) {
24467 * resolve('Hello, ' + name + '!');
24469 * reject('Greeting ' + name + ' is not allowed.');
24475 * var promise = asyncGreet('Robin Hood');
24476 * promise.then(function(greeting) {
24477 * alert('Success: ' + greeting);
24478 * }, function(reason) {
24479 * alert('Failed: ' + reason);
24483 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
24485 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
24487 * However, the more traditional CommonJS-style usage is still available, and documented below.
24489 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
24490 * interface for interacting with an object that represents the result of an action that is
24491 * performed asynchronously, and may or may not be finished at any given point in time.
24493 * From the perspective of dealing with error handling, deferred and promise APIs are to
24494 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
24497 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
24498 * // are available in the current lexical scope (they could have been injected or passed in).
24500 * function asyncGreet(name) {
24501 * var deferred = $q.defer();
24503 * setTimeout(function() {
24504 * deferred.notify('About to greet ' + name + '.');
24506 * if (okToGreet(name)) {
24507 * deferred.resolve('Hello, ' + name + '!');
24509 * deferred.reject('Greeting ' + name + ' is not allowed.');
24513 * return deferred.promise;
24516 * var promise = asyncGreet('Robin Hood');
24517 * promise.then(function(greeting) {
24518 * alert('Success: ' + greeting);
24519 * }, function(reason) {
24520 * alert('Failed: ' + reason);
24521 * }, function(update) {
24522 * alert('Got notification: ' + update);
24526 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
24527 * comes in the way of guarantees that promise and deferred APIs make, see
24528 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
24530 * Additionally the promise api allows for composition that is very hard to do with the
24531 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
24532 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
24533 * section on serial or parallel joining of promises.
24535 * # The Deferred API
24537 * A new instance of deferred is constructed by calling `$q.defer()`.
24539 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
24540 * that can be used for signaling the successful or unsuccessful completion, as well as the status
24545 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
24546 * constructed via `$q.reject`, the promise will be rejected instead.
24547 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
24548 * resolving it with a rejection constructed via `$q.reject`.
24549 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
24550 * multiple times before the promise is either resolved or rejected.
24554 * - promise – `{Promise}` – promise object associated with this deferred.
24557 * # The Promise API
24559 * A new promise instance is created when a deferred instance is created and can be retrieved by
24560 * calling `deferred.promise`.
24562 * The purpose of the promise object is to allow for interested parties to get access to the result
24563 * of the deferred task when it completes.
24567 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
24568 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
24569 * as soon as the result is available. The callbacks are called with a single argument: the result
24570 * or rejection reason. Additionally, the notify callback may be called zero or more times to
24571 * provide a progress indication, before the promise is resolved or rejected.
24573 * This method *returns a new promise* which is resolved or rejected via the return value of the
24574 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
24575 * with the value which is resolved in that promise using
24576 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
24577 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
24578 * resolved or rejected from the notifyCallback method.
24580 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
24582 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
24583 * but to do so without modifying the final value. This is useful to release resources or do some
24584 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
24585 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
24586 * more information.
24588 * # Chaining promises
24590 * Because calling the `then` method of a promise returns a new derived promise, it is easily
24591 * possible to create a chain of promises:
24594 * promiseB = promiseA.then(function(result) {
24595 * return result + 1;
24598 * // promiseB will be resolved immediately after promiseA is resolved and its value
24599 * // will be the result of promiseA incremented by 1
24602 * It is possible to create chains of any length and since a promise can be resolved with another
24603 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
24604 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
24605 * $http's response interceptors.
24608 * # Differences between Kris Kowal's Q and $q
24610 * There are two main differences:
24612 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
24613 * mechanism in angular, which means faster propagation of resolution or rejection into your
24614 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
24615 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
24616 * all the important functionality needed for common async tasks.
24621 * it('should simulate promise', inject(function($q, $rootScope) {
24622 * var deferred = $q.defer();
24623 * var promise = deferred.promise;
24624 * var resolvedValue;
24626 * promise.then(function(value) { resolvedValue = value; });
24627 * expect(resolvedValue).toBeUndefined();
24629 * // Simulate resolving of promise
24630 * deferred.resolve(123);
24631 * // Note that the 'then' function does not get called synchronously.
24632 * // This is because we want the promise API to always be async, whether or not
24633 * // it got called synchronously or asynchronously.
24634 * expect(resolvedValue).toBeUndefined();
24636 * // Propagate promise resolution to 'then' functions using $apply().
24637 * $rootScope.$apply();
24638 * expect(resolvedValue).toEqual(123);
24642 * @param {function(function, function)} resolver Function which is responsible for resolving or
24643 * rejecting the newly created promise. The first parameter is a function which resolves the
24644 * promise, the second parameter is a function which rejects the promise.
24646 * @returns {Promise} The newly created promise.
24648 function $QProvider() {
24650 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
24651 return qFactory(function(callback) {
24652 $rootScope.$evalAsync(callback);
24653 }, $exceptionHandler);
24657 function $$QProvider() {
24658 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
24659 return qFactory(function(callback) {
24660 $browser.defer(callback);
24661 }, $exceptionHandler);
24666 * Constructs a promise manager.
24668 * @param {function(function)} nextTick Function for executing functions in the next turn.
24669 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
24670 * debugging purposes.
24671 * @returns {object} Promise manager.
24673 function qFactory(nextTick, exceptionHandler) {
24674 var $qMinErr = minErr('$q', TypeError);
24678 * @name ng.$q#defer
24682 * Creates a `Deferred` object which represents a task which will finish in the future.
24684 * @returns {Deferred} Returns a new instance of deferred.
24686 var defer = function() {
24687 var d = new Deferred();
24688 //Necessary to support unbound execution :/
24689 d.resolve = simpleBind(d, d.resolve);
24690 d.reject = simpleBind(d, d.reject);
24691 d.notify = simpleBind(d, d.notify);
24695 function Promise() {
24696 this.$$state = { status: 0 };
24699 extend(Promise.prototype, {
24700 then: function(onFulfilled, onRejected, progressBack) {
24701 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
24704 var result = new Deferred();
24706 this.$$state.pending = this.$$state.pending || [];
24707 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
24708 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
24710 return result.promise;
24713 "catch": function(callback) {
24714 return this.then(null, callback);
24717 "finally": function(callback, progressBack) {
24718 return this.then(function(value) {
24719 return handleCallback(value, true, callback);
24720 }, function(error) {
24721 return handleCallback(error, false, callback);
24726 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
24727 function simpleBind(context, fn) {
24728 return function(value) {
24729 fn.call(context, value);
24733 function processQueue(state) {
24734 var fn, deferred, pending;
24736 pending = state.pending;
24737 state.processScheduled = false;
24738 state.pending = undefined;
24739 for (var i = 0, ii = pending.length; i < ii; ++i) {
24740 deferred = pending[i][0];
24741 fn = pending[i][state.status];
24743 if (isFunction(fn)) {
24744 deferred.resolve(fn(state.value));
24745 } else if (state.status === 1) {
24746 deferred.resolve(state.value);
24748 deferred.reject(state.value);
24751 deferred.reject(e);
24752 exceptionHandler(e);
24757 function scheduleProcessQueue(state) {
24758 if (state.processScheduled || !state.pending) return;
24759 state.processScheduled = true;
24760 nextTick(function() { processQueue(state); });
24763 function Deferred() {
24764 this.promise = new Promise();
24767 extend(Deferred.prototype, {
24768 resolve: function(val) {
24769 if (this.promise.$$state.status) return;
24770 if (val === this.promise) {
24771 this.$$reject($qMinErr(
24773 "Expected promise to be resolved with value other than itself '{0}'",
24776 this.$$resolve(val);
24781 $$resolve: function(val) {
24786 if ((isObject(val) || isFunction(val))) then = val && val.then;
24787 if (isFunction(then)) {
24788 this.promise.$$state.status = -1;
24789 then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
24791 this.promise.$$state.value = val;
24792 this.promise.$$state.status = 1;
24793 scheduleProcessQueue(this.promise.$$state);
24797 exceptionHandler(e);
24800 function resolvePromise(val) {
24803 that.$$resolve(val);
24805 function rejectPromise(val) {
24808 that.$$reject(val);
24812 reject: function(reason) {
24813 if (this.promise.$$state.status) return;
24814 this.$$reject(reason);
24817 $$reject: function(reason) {
24818 this.promise.$$state.value = reason;
24819 this.promise.$$state.status = 2;
24820 scheduleProcessQueue(this.promise.$$state);
24823 notify: function(progress) {
24824 var callbacks = this.promise.$$state.pending;
24826 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
24827 nextTick(function() {
24828 var callback, result;
24829 for (var i = 0, ii = callbacks.length; i < ii; i++) {
24830 result = callbacks[i][0];
24831 callback = callbacks[i][3];
24833 result.notify(isFunction(callback) ? callback(progress) : progress);
24835 exceptionHandler(e);
24849 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
24850 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
24851 * a promise chain, you don't need to worry about it.
24853 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
24854 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
24855 * a promise error callback and you want to forward the error to the promise derived from the
24856 * current promise, you have to "rethrow" the error by returning a rejection constructed via
24860 * promiseB = promiseA.then(function(result) {
24861 * // success: do something and resolve promiseB
24862 * // with the old or a new result
24864 * }, function(reason) {
24865 * // error: handle the error if possible and
24866 * // resolve promiseB with newPromiseOrValue,
24867 * // otherwise forward the rejection to promiseB
24868 * if (canHandle(reason)) {
24869 * // handle the error and recover
24870 * return newPromiseOrValue;
24872 * return $q.reject(reason);
24876 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
24877 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
24879 var reject = function(reason) {
24880 var result = new Deferred();
24881 result.reject(reason);
24882 return result.promise;
24885 var makePromise = function makePromise(value, resolved) {
24886 var result = new Deferred();
24888 result.resolve(value);
24890 result.reject(value);
24892 return result.promise;
24895 var handleCallback = function handleCallback(value, isResolved, callback) {
24896 var callbackOutput = null;
24898 if (isFunction(callback)) callbackOutput = callback();
24900 return makePromise(e, false);
24902 if (isPromiseLike(callbackOutput)) {
24903 return callbackOutput.then(function() {
24904 return makePromise(value, isResolved);
24905 }, function(error) {
24906 return makePromise(error, false);
24909 return makePromise(value, isResolved);
24919 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
24920 * This is useful when you are dealing with an object that might or might not be a promise, or if
24921 * the promise comes from a source that can't be trusted.
24923 * @param {*} value Value or a promise
24924 * @param {Function=} successCallback
24925 * @param {Function=} errorCallback
24926 * @param {Function=} progressCallback
24927 * @returns {Promise} Returns a promise of the passed value or promise
24931 var when = function(value, callback, errback, progressBack) {
24932 var result = new Deferred();
24933 result.resolve(value);
24934 return result.promise.then(callback, errback, progressBack);
24943 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
24945 * @param {*} value Value or a promise
24946 * @param {Function=} successCallback
24947 * @param {Function=} errorCallback
24948 * @param {Function=} progressCallback
24949 * @returns {Promise} Returns a promise of the passed value or promise
24951 var resolve = when;
24959 * Combines multiple promises into a single promise that is resolved when all of the input
24960 * promises are resolved.
24962 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
24963 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
24964 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
24965 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
24966 * with the same rejection value.
24969 function all(promises) {
24970 var deferred = new Deferred(),
24972 results = isArray(promises) ? [] : {};
24974 forEach(promises, function(promise, key) {
24976 when(promise).then(function(value) {
24977 if (results.hasOwnProperty(key)) return;
24978 results[key] = value;
24979 if (!(--counter)) deferred.resolve(results);
24980 }, function(reason) {
24981 if (results.hasOwnProperty(key)) return;
24982 deferred.reject(reason);
24986 if (counter === 0) {
24987 deferred.resolve(results);
24990 return deferred.promise;
24993 var $Q = function Q(resolver) {
24994 if (!isFunction(resolver)) {
24995 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
24998 var deferred = new Deferred();
25000 function resolveFn(value) {
25001 deferred.resolve(value);
25004 function rejectFn(reason) {
25005 deferred.reject(reason);
25008 resolver(resolveFn, rejectFn);
25010 return deferred.promise;
25013 // Let's make the instanceof operator work for promises, so that
25014 // `new $q(fn) instanceof $q` would evaluate to true.
25015 $Q.prototype = Promise.prototype;
25018 $Q.reject = reject;
25020 $Q.resolve = resolve;
25026 function $$RAFProvider() { //rAF
25027 this.$get = ['$window', '$timeout', function($window, $timeout) {
25028 var requestAnimationFrame = $window.requestAnimationFrame ||
25029 $window.webkitRequestAnimationFrame;
25031 var cancelAnimationFrame = $window.cancelAnimationFrame ||
25032 $window.webkitCancelAnimationFrame ||
25033 $window.webkitCancelRequestAnimationFrame;
25035 var rafSupported = !!requestAnimationFrame;
25036 var raf = rafSupported
25038 var id = requestAnimationFrame(fn);
25039 return function() {
25040 cancelAnimationFrame(id);
25044 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
25045 return function() {
25046 $timeout.cancel(timer);
25050 raf.supported = rafSupported;
25059 * The design decisions behind the scope are heavily favored for speed and memory consumption.
25061 * The typical use of scope is to watch the expressions, which most of the time return the same
25062 * value as last time so we optimize the operation.
25064 * Closures construction is expensive in terms of speed as well as memory:
25065 * - No closures, instead use prototypical inheritance for API
25066 * - Internal state needs to be stored on scope directly, which means that private state is
25067 * exposed as $$____ properties
25069 * Loop operations are optimized by using while(count--) { ... }
25070 * - This means that in order to keep the same order of execution as addition we have to add
25071 * items to the array at the beginning (unshift) instead of at the end (push)
25073 * Child scopes are created and removed often
25074 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
25076 * There are fewer watches than observers. This is why you don't want the observer to be implemented
25077 * in the same way as watch. Watch requires return of the initialization function which is expensive
25084 * @name $rootScopeProvider
25087 * Provider for the $rootScope service.
25092 * @name $rootScopeProvider#digestTtl
25095 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
25096 * assuming that the model is unstable.
25098 * The current default is 10 iterations.
25100 * In complex applications it's possible that the dependencies between `$watch`s will result in
25101 * several digest iterations. However if an application needs more than the default 10 digest
25102 * iterations for its model to stabilize then you should investigate what is causing the model to
25103 * continuously change during the digest.
25105 * Increasing the TTL could have performance implications, so you should not change it without
25106 * proper justification.
25108 * @param {number} limit The number of digest iterations.
25117 * Every application has a single root {@link ng.$rootScope.Scope scope}.
25118 * All other scopes are descendant scopes of the root scope. Scopes provide separation
25119 * between the model and the view, via a mechanism for watching the model for changes.
25120 * They also provide event emission/broadcast and subscription facility. See the
25121 * {@link guide/scope developer guide on scopes}.
25123 function $RootScopeProvider() {
25125 var $rootScopeMinErr = minErr('$rootScope');
25126 var lastDirtyWatch = null;
25127 var applyAsyncId = null;
25129 this.digestTtl = function(value) {
25130 if (arguments.length) {
25136 function createChildScopeClass(parent) {
25137 function ChildScope() {
25138 this.$$watchers = this.$$nextSibling =
25139 this.$$childHead = this.$$childTail = null;
25140 this.$$listeners = {};
25141 this.$$listenerCount = {};
25142 this.$$watchersCount = 0;
25143 this.$id = nextUid();
25144 this.$$ChildScope = null;
25146 ChildScope.prototype = parent;
25150 this.$get = ['$exceptionHandler', '$parse', '$browser',
25151 function($exceptionHandler, $parse, $browser) {
25153 function destroyChildScope($event) {
25154 $event.currentScope.$$destroyed = true;
25157 function cleanUpScope($scope) {
25160 // There is a memory leak in IE9 if all child scopes are not disconnected
25161 // completely when a scope is destroyed. So this code will recurse up through
25162 // all this scopes children
25164 // See issue https://github.com/angular/angular.js/issues/10706
25165 $scope.$$childHead && cleanUpScope($scope.$$childHead);
25166 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
25169 // The code below works around IE9 and V8's memory leaks
25172 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
25173 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
25174 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
25176 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
25177 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
25182 * @name $rootScope.Scope
25185 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
25186 * {@link auto.$injector $injector}. Child scopes are created using the
25187 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
25188 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
25189 * an in-depth introduction and usage examples.
25193 * A scope can inherit from a parent scope, as in this example:
25195 var parent = $rootScope;
25196 var child = parent.$new();
25198 parent.salutation = "Hello";
25199 expect(child.salutation).toEqual('Hello');
25201 child.salutation = "Welcome";
25202 expect(child.salutation).toEqual('Welcome');
25203 expect(parent.salutation).toEqual('Hello');
25206 * When interacting with `Scope` in tests, additional helper methods are available on the
25207 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
25211 * @param {Object.<string, function()>=} providers Map of service factory which need to be
25212 * provided for the current scope. Defaults to {@link ng}.
25213 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
25214 * append/override services provided by `providers`. This is handy
25215 * when unit-testing and having the need to override a default
25217 * @returns {Object} Newly created scope.
25221 this.$id = nextUid();
25222 this.$$phase = this.$parent = this.$$watchers =
25223 this.$$nextSibling = this.$$prevSibling =
25224 this.$$childHead = this.$$childTail = null;
25226 this.$$destroyed = false;
25227 this.$$listeners = {};
25228 this.$$listenerCount = {};
25229 this.$$watchersCount = 0;
25230 this.$$isolateBindings = null;
25235 * @name $rootScope.Scope#$id
25238 * Unique scope ID (monotonically increasing) useful for debugging.
25243 * @name $rootScope.Scope#$parent
25246 * Reference to the parent scope.
25251 * @name $rootScope.Scope#$root
25254 * Reference to the root scope.
25257 Scope.prototype = {
25258 constructor: Scope,
25261 * @name $rootScope.Scope#$new
25265 * Creates a new child {@link ng.$rootScope.Scope scope}.
25267 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
25268 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
25270 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
25271 * desired for the scope and its child scopes to be permanently detached from the parent and
25272 * thus stop participating in model change detection and listener notification by invoking.
25274 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
25275 * parent scope. The scope is isolated, as it can not see parent scope properties.
25276 * When creating widgets, it is useful for the widget to not accidentally read parent
25279 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
25280 * of the newly created scope. Defaults to `this` scope if not provided.
25281 * This is used when creating a transclude scope to correctly place it
25282 * in the scope hierarchy while maintaining the correct prototypical
25285 * @returns {Object} The newly created child scope.
25288 $new: function(isolate, parent) {
25291 parent = parent || this;
25294 child = new Scope();
25295 child.$root = this.$root;
25297 // Only create a child scope class if somebody asks for one,
25298 // but cache it to allow the VM to optimize lookups.
25299 if (!this.$$ChildScope) {
25300 this.$$ChildScope = createChildScopeClass(this);
25302 child = new this.$$ChildScope();
25304 child.$parent = parent;
25305 child.$$prevSibling = parent.$$childTail;
25306 if (parent.$$childHead) {
25307 parent.$$childTail.$$nextSibling = child;
25308 parent.$$childTail = child;
25310 parent.$$childHead = parent.$$childTail = child;
25313 // When the new scope is not isolated or we inherit from `this`, and
25314 // the parent scope is destroyed, the property `$$destroyed` is inherited
25315 // prototypically. In all other cases, this property needs to be set
25316 // when the parent scope is destroyed.
25317 // The listener needs to be added after the parent is set
25318 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
25325 * @name $rootScope.Scope#$watch
25329 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
25331 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
25332 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
25333 * its value when executed multiple times with the same input because it may be executed multiple
25334 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
25335 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
25336 * - The `listener` is called only when the value from the current `watchExpression` and the
25337 * previous call to `watchExpression` are not equal (with the exception of the initial run,
25338 * see below). Inequality is determined according to reference inequality,
25339 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
25340 * via the `!==` Javascript operator, unless `objectEquality == true`
25342 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
25343 * according to the {@link angular.equals} function. To save the value of the object for
25344 * later comparison, the {@link angular.copy} function is used. This therefore means that
25345 * watching complex objects will have adverse memory and performance implications.
25346 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
25347 * This is achieved by rerunning the watchers until no changes are detected. The rerun
25348 * iteration limit is 10 to prevent an infinite loop deadlock.
25351 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
25352 * you can register a `watchExpression` function with no `listener`. (Be prepared for
25353 * multiple calls to your `watchExpression` because it will execute multiple times in a
25354 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
25356 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
25357 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
25358 * watcher. In rare cases, this is undesirable because the listener is called when the result
25359 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
25360 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
25361 * listener was called due to initialization.
25367 // let's assume that scope was dependency injected as the $rootScope
25368 var scope = $rootScope;
25369 scope.name = 'misko';
25372 expect(scope.counter).toEqual(0);
25373 scope.$watch('name', function(newValue, oldValue) {
25374 scope.counter = scope.counter + 1;
25376 expect(scope.counter).toEqual(0);
25379 // the listener is always called during the first $digest loop after it was registered
25380 expect(scope.counter).toEqual(1);
25383 // but now it will not be called unless the value changes
25384 expect(scope.counter).toEqual(1);
25386 scope.name = 'adam';
25388 expect(scope.counter).toEqual(2);
25392 // Using a function as a watchExpression
25394 scope.foodCounter = 0;
25395 expect(scope.foodCounter).toEqual(0);
25397 // This function returns the value being watched. It is called for each turn of the $digest loop
25398 function() { return food; },
25399 // This is the change listener, called when the value returned from the above function changes
25400 function(newValue, oldValue) {
25401 if ( newValue !== oldValue ) {
25402 // Only increment the counter if the value changed
25403 scope.foodCounter = scope.foodCounter + 1;
25407 // No digest has been run so the counter will be zero
25408 expect(scope.foodCounter).toEqual(0);
25410 // Run the digest but since food has not changed count will still be zero
25412 expect(scope.foodCounter).toEqual(0);
25414 // Update food and run digest. Now the counter will increment
25415 food = 'cheeseburger';
25417 expect(scope.foodCounter).toEqual(1);
25423 * @param {(function()|string)} watchExpression Expression that is evaluated on each
25424 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
25425 * a call to the `listener`.
25427 * - `string`: Evaluated as {@link guide/expression expression}
25428 * - `function(scope)`: called with current `scope` as a parameter.
25429 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
25430 * of `watchExpression` changes.
25432 * - `newVal` contains the current value of the `watchExpression`
25433 * - `oldVal` contains the previous value of the `watchExpression`
25434 * - `scope` refers to the current scope
25435 * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
25436 * comparing for reference equality.
25437 * @returns {function()} Returns a deregistration function for this listener.
25439 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
25440 var get = $parse(watchExp);
25442 if (get.$$watchDelegate) {
25443 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
25446 array = scope.$$watchers,
25449 last: initWatchVal,
25451 exp: prettyPrintExpression || watchExp,
25452 eq: !!objectEquality
25455 lastDirtyWatch = null;
25457 if (!isFunction(listener)) {
25462 array = scope.$$watchers = [];
25464 // we use unshift since we use a while loop in $digest for speed.
25465 // the while loop reads in reverse order.
25466 array.unshift(watcher);
25467 incrementWatchersCount(this, 1);
25469 return function deregisterWatch() {
25470 if (arrayRemove(array, watcher) >= 0) {
25471 incrementWatchersCount(scope, -1);
25473 lastDirtyWatch = null;
25479 * @name $rootScope.Scope#$watchGroup
25483 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
25484 * If any one expression in the collection changes the `listener` is executed.
25486 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
25487 * call to $digest() to see if any items changes.
25488 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
25490 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
25491 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
25493 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
25494 * expression in `watchExpressions` changes
25495 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
25496 * those of `watchExpression`
25497 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
25498 * those of `watchExpression`
25499 * The `scope` refers to the current scope.
25500 * @returns {function()} Returns a de-registration function for all listeners.
25502 $watchGroup: function(watchExpressions, listener) {
25503 var oldValues = new Array(watchExpressions.length);
25504 var newValues = new Array(watchExpressions.length);
25505 var deregisterFns = [];
25507 var changeReactionScheduled = false;
25508 var firstRun = true;
25510 if (!watchExpressions.length) {
25511 // No expressions means we call the listener ASAP
25512 var shouldCall = true;
25513 self.$evalAsync(function() {
25514 if (shouldCall) listener(newValues, newValues, self);
25516 return function deregisterWatchGroup() {
25517 shouldCall = false;
25521 if (watchExpressions.length === 1) {
25522 // Special case size of one
25523 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
25524 newValues[0] = value;
25525 oldValues[0] = oldValue;
25526 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
25530 forEach(watchExpressions, function(expr, i) {
25531 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
25532 newValues[i] = value;
25533 oldValues[i] = oldValue;
25534 if (!changeReactionScheduled) {
25535 changeReactionScheduled = true;
25536 self.$evalAsync(watchGroupAction);
25539 deregisterFns.push(unwatchFn);
25542 function watchGroupAction() {
25543 changeReactionScheduled = false;
25547 listener(newValues, newValues, self);
25549 listener(newValues, oldValues, self);
25553 return function deregisterWatchGroup() {
25554 while (deregisterFns.length) {
25555 deregisterFns.shift()();
25563 * @name $rootScope.Scope#$watchCollection
25567 * Shallow watches the properties of an object and fires whenever any of the properties change
25568 * (for arrays, this implies watching the array items; for object maps, this implies watching
25569 * the properties). If a change is detected, the `listener` callback is fired.
25571 * - The `obj` collection is observed via standard $watch operation and is examined on every
25572 * call to $digest() to see if any items have been added, removed, or moved.
25573 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
25574 * adding, removing, and moving items belonging to an object or array.
25579 $scope.names = ['igor', 'matias', 'misko', 'james'];
25580 $scope.dataCount = 4;
25582 $scope.$watchCollection('names', function(newNames, oldNames) {
25583 $scope.dataCount = newNames.length;
25586 expect($scope.dataCount).toEqual(4);
25589 //still at 4 ... no changes
25590 expect($scope.dataCount).toEqual(4);
25592 $scope.names.pop();
25595 //now there's been a change
25596 expect($scope.dataCount).toEqual(3);
25600 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
25601 * expression value should evaluate to an object or an array which is observed on each
25602 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
25603 * collection will trigger a call to the `listener`.
25605 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
25606 * when a change is detected.
25607 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
25608 * - The `oldCollection` object is a copy of the former collection data.
25609 * Due to performance considerations, the`oldCollection` value is computed only if the
25610 * `listener` function declares two or more arguments.
25611 * - The `scope` argument refers to the current scope.
25613 * @returns {function()} Returns a de-registration function for this listener. When the
25614 * de-registration function is executed, the internal watch operation is terminated.
25616 $watchCollection: function(obj, listener) {
25617 $watchCollectionInterceptor.$stateful = true;
25620 // the current value, updated on each dirty-check run
25622 // a shallow copy of the newValue from the last dirty-check run,
25623 // updated to match newValue during dirty-check run
25625 // a shallow copy of the newValue from when the last change happened
25627 // only track veryOldValue if the listener is asking for it
25628 var trackVeryOldValue = (listener.length > 1);
25629 var changeDetected = 0;
25630 var changeDetector = $parse(obj, $watchCollectionInterceptor);
25631 var internalArray = [];
25632 var internalObject = {};
25633 var initRun = true;
25636 function $watchCollectionInterceptor(_value) {
25638 var newLength, key, bothNaN, newItem, oldItem;
25640 // If the new value is undefined, then return undefined as the watch may be a one-time watch
25641 if (isUndefined(newValue)) return;
25643 if (!isObject(newValue)) { // if primitive
25644 if (oldValue !== newValue) {
25645 oldValue = newValue;
25648 } else if (isArrayLike(newValue)) {
25649 if (oldValue !== internalArray) {
25650 // we are transitioning from something which was not an array into array.
25651 oldValue = internalArray;
25652 oldLength = oldValue.length = 0;
25656 newLength = newValue.length;
25658 if (oldLength !== newLength) {
25659 // if lengths do not match we need to trigger change notification
25661 oldValue.length = oldLength = newLength;
25663 // copy the items to oldValue and look for changes.
25664 for (var i = 0; i < newLength; i++) {
25665 oldItem = oldValue[i];
25666 newItem = newValue[i];
25668 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
25669 if (!bothNaN && (oldItem !== newItem)) {
25671 oldValue[i] = newItem;
25675 if (oldValue !== internalObject) {
25676 // we are transitioning from something which was not an object into object.
25677 oldValue = internalObject = {};
25681 // copy the items to oldValue and look for changes.
25683 for (key in newValue) {
25684 if (hasOwnProperty.call(newValue, key)) {
25686 newItem = newValue[key];
25687 oldItem = oldValue[key];
25689 if (key in oldValue) {
25690 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
25691 if (!bothNaN && (oldItem !== newItem)) {
25693 oldValue[key] = newItem;
25697 oldValue[key] = newItem;
25702 if (oldLength > newLength) {
25703 // we used to have more keys, need to find them and destroy them.
25705 for (key in oldValue) {
25706 if (!hasOwnProperty.call(newValue, key)) {
25708 delete oldValue[key];
25713 return changeDetected;
25716 function $watchCollectionAction() {
25719 listener(newValue, newValue, self);
25721 listener(newValue, veryOldValue, self);
25724 // make a copy for the next time a collection is changed
25725 if (trackVeryOldValue) {
25726 if (!isObject(newValue)) {
25728 veryOldValue = newValue;
25729 } else if (isArrayLike(newValue)) {
25730 veryOldValue = new Array(newValue.length);
25731 for (var i = 0; i < newValue.length; i++) {
25732 veryOldValue[i] = newValue[i];
25734 } else { // if object
25736 for (var key in newValue) {
25737 if (hasOwnProperty.call(newValue, key)) {
25738 veryOldValue[key] = newValue[key];
25745 return this.$watch(changeDetector, $watchCollectionAction);
25750 * @name $rootScope.Scope#$digest
25754 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
25755 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
25756 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
25757 * until no more listeners are firing. This means that it is possible to get into an infinite
25758 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
25759 * iterations exceeds 10.
25761 * Usually, you don't call `$digest()` directly in
25762 * {@link ng.directive:ngController controllers} or in
25763 * {@link ng.$compileProvider#directive directives}.
25764 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
25765 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
25767 * If you want to be notified whenever `$digest()` is called,
25768 * you can register a `watchExpression` function with
25769 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
25771 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
25776 scope.name = 'misko';
25779 expect(scope.counter).toEqual(0);
25780 scope.$watch('name', function(newValue, oldValue) {
25781 scope.counter = scope.counter + 1;
25783 expect(scope.counter).toEqual(0);
25786 // the listener is always called during the first $digest loop after it was registered
25787 expect(scope.counter).toEqual(1);
25790 // but now it will not be called unless the value changes
25791 expect(scope.counter).toEqual(1);
25793 scope.name = 'adam';
25795 expect(scope.counter).toEqual(2);
25799 $digest: function() {
25800 var watch, value, last, fn, get,
25804 next, current, target = this,
25806 logIdx, logMsg, asyncTask;
25808 beginPhase('$digest');
25809 // Check for changes to browser url that happened in sync before the call to $digest
25810 $browser.$$checkUrlChange();
25812 if (this === $rootScope && applyAsyncId !== null) {
25813 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
25814 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
25815 $browser.defer.cancel(applyAsyncId);
25819 lastDirtyWatch = null;
25821 do { // "while dirty" loop
25825 while (asyncQueue.length) {
25827 asyncTask = asyncQueue.shift();
25828 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
25830 $exceptionHandler(e);
25832 lastDirtyWatch = null;
25835 traverseScopesLoop:
25836 do { // "traverse the scopes" loop
25837 if ((watchers = current.$$watchers)) {
25838 // process our watches
25839 length = watchers.length;
25842 watch = watchers[length];
25843 // Most common watches are on primitives, in which case we can short
25844 // circuit it with === operator, only when === fails do we use .equals
25847 if ((value = get(current)) !== (last = watch.last) &&
25849 ? equals(value, last)
25850 : (typeof value === 'number' && typeof last === 'number'
25851 && isNaN(value) && isNaN(last)))) {
25853 lastDirtyWatch = watch;
25854 watch.last = watch.eq ? copy(value, null) : value;
25856 fn(value, ((last === initWatchVal) ? value : last), current);
25859 if (!watchLog[logIdx]) watchLog[logIdx] = [];
25860 watchLog[logIdx].push({
25861 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
25866 } else if (watch === lastDirtyWatch) {
25867 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
25868 // have already been tested.
25870 break traverseScopesLoop;
25874 $exceptionHandler(e);
25879 // Insanity Warning: scope depth-first traversal
25880 // yes, this code is a bit crazy, but it works and we have tests to prove it!
25881 // this piece should be kept in sync with the traversal in $broadcast
25882 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
25883 (current !== target && current.$$nextSibling)))) {
25884 while (current !== target && !(next = current.$$nextSibling)) {
25885 current = current.$parent;
25888 } while ((current = next));
25890 // `break traverseScopesLoop;` takes us to here
25892 if ((dirty || asyncQueue.length) && !(ttl--)) {
25894 throw $rootScopeMinErr('infdig',
25895 '{0} $digest() iterations reached. Aborting!\n' +
25896 'Watchers fired in the last 5 iterations: {1}',
25900 } while (dirty || asyncQueue.length);
25904 while (postDigestQueue.length) {
25906 postDigestQueue.shift()();
25908 $exceptionHandler(e);
25916 * @name $rootScope.Scope#$destroy
25917 * @eventType broadcast on scope being destroyed
25920 * Broadcasted when a scope and its children are being destroyed.
25922 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
25923 * clean up DOM bindings before an element is removed from the DOM.
25928 * @name $rootScope.Scope#$destroy
25932 * Removes the current scope (and all of its children) from the parent scope. Removal implies
25933 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
25934 * propagate to the current scope and its children. Removal also implies that the current
25935 * scope is eligible for garbage collection.
25937 * The `$destroy()` is usually used by directives such as
25938 * {@link ng.directive:ngRepeat ngRepeat} for managing the
25939 * unrolling of the loop.
25941 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
25942 * Application code can register a `$destroy` event handler that will give it a chance to
25943 * perform any necessary cleanup.
25945 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
25946 * clean up DOM bindings before an element is removed from the DOM.
25948 $destroy: function() {
25949 // We can't destroy a scope that has been already destroyed.
25950 if (this.$$destroyed) return;
25951 var parent = this.$parent;
25953 this.$broadcast('$destroy');
25954 this.$$destroyed = true;
25956 if (this === $rootScope) {
25957 //Remove handlers attached to window when $rootScope is removed
25958 $browser.$$applicationDestroyed();
25961 incrementWatchersCount(this, -this.$$watchersCount);
25962 for (var eventName in this.$$listenerCount) {
25963 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
25966 // sever all the references to parent scopes (after this cleanup, the current scope should
25967 // not be retained by any of our references and should be eligible for garbage collection)
25968 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
25969 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
25970 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
25971 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
25973 // Disable listeners, watchers and apply/digest methods
25974 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
25975 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
25976 this.$$listeners = {};
25978 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
25979 this.$$nextSibling = null;
25980 cleanUpScope(this);
25985 * @name $rootScope.Scope#$eval
25989 * Executes the `expression` on the current scope and returns the result. Any exceptions in
25990 * the expression are propagated (uncaught). This is useful when evaluating Angular
25995 var scope = ng.$rootScope.Scope();
25999 expect(scope.$eval('a+b')).toEqual(3);
26000 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
26003 * @param {(string|function())=} expression An angular expression to be executed.
26005 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26006 * - `function(scope)`: execute the function with the current `scope` parameter.
26008 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
26009 * @returns {*} The result of evaluating the expression.
26011 $eval: function(expr, locals) {
26012 return $parse(expr)(this, locals);
26017 * @name $rootScope.Scope#$evalAsync
26021 * Executes the expression on the current scope at a later point in time.
26023 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
26026 * - it will execute after the function that scheduled the evaluation (preferably before DOM
26028 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
26029 * `expression` execution.
26031 * Any exceptions from the execution of the expression are forwarded to the
26032 * {@link ng.$exceptionHandler $exceptionHandler} service.
26034 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
26035 * will be scheduled. However, it is encouraged to always call code that changes the model
26036 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
26038 * @param {(string|function())=} expression An angular expression to be executed.
26040 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26041 * - `function(scope)`: execute the function with the current `scope` parameter.
26043 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
26045 $evalAsync: function(expr, locals) {
26046 // if we are outside of an $digest loop and this is the first time we are scheduling async
26047 // task also schedule async auto-flush
26048 if (!$rootScope.$$phase && !asyncQueue.length) {
26049 $browser.defer(function() {
26050 if (asyncQueue.length) {
26051 $rootScope.$digest();
26056 asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
26059 $$postDigest: function(fn) {
26060 postDigestQueue.push(fn);
26065 * @name $rootScope.Scope#$apply
26069 * `$apply()` is used to execute an expression in angular from outside of the angular
26070 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
26071 * Because we are calling into the angular framework we need to perform proper scope life
26072 * cycle of {@link ng.$exceptionHandler exception handling},
26073 * {@link ng.$rootScope.Scope#$digest executing watches}.
26077 * # Pseudo-Code of `$apply()`
26079 function $apply(expr) {
26081 return $eval(expr);
26083 $exceptionHandler(e);
26091 * Scope's `$apply()` method transitions through the following stages:
26093 * 1. The {@link guide/expression expression} is executed using the
26094 * {@link ng.$rootScope.Scope#$eval $eval()} method.
26095 * 2. Any exceptions from the execution of the expression are forwarded to the
26096 * {@link ng.$exceptionHandler $exceptionHandler} service.
26097 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
26098 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
26101 * @param {(string|function())=} exp An angular expression to be executed.
26103 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26104 * - `function(scope)`: execute the function with current `scope` parameter.
26106 * @returns {*} The result of evaluating the expression.
26108 $apply: function(expr) {
26110 beginPhase('$apply');
26112 return this.$eval(expr);
26117 $exceptionHandler(e);
26120 $rootScope.$digest();
26122 $exceptionHandler(e);
26130 * @name $rootScope.Scope#$applyAsync
26134 * Schedule the invocation of $apply to occur at a later time. The actual time difference
26135 * varies across browsers, but is typically around ~10 milliseconds.
26137 * This can be used to queue up multiple expressions which need to be evaluated in the same
26140 * @param {(string|function())=} exp An angular expression to be executed.
26142 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
26143 * - `function(scope)`: execute the function with current `scope` parameter.
26145 $applyAsync: function(expr) {
26147 expr && applyAsyncQueue.push($applyAsyncExpression);
26148 expr = $parse(expr);
26149 scheduleApplyAsync();
26151 function $applyAsyncExpression() {
26158 * @name $rootScope.Scope#$on
26162 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
26163 * discussion of event life cycle.
26165 * The event listener function format is: `function(event, args...)`. The `event` object
26166 * passed into the listener has the following attributes:
26168 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
26170 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
26171 * event propagates through the scope hierarchy, this property is set to null.
26172 * - `name` - `{string}`: name of the event.
26173 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
26174 * further event propagation (available only for events that were `$emit`-ed).
26175 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
26177 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
26179 * @param {string} name Event name to listen on.
26180 * @param {function(event, ...args)} listener Function to call when the event is emitted.
26181 * @returns {function()} Returns a deregistration function for this listener.
26183 $on: function(name, listener) {
26184 var namedListeners = this.$$listeners[name];
26185 if (!namedListeners) {
26186 this.$$listeners[name] = namedListeners = [];
26188 namedListeners.push(listener);
26190 var current = this;
26192 if (!current.$$listenerCount[name]) {
26193 current.$$listenerCount[name] = 0;
26195 current.$$listenerCount[name]++;
26196 } while ((current = current.$parent));
26199 return function() {
26200 var indexOfListener = namedListeners.indexOf(listener);
26201 if (indexOfListener !== -1) {
26202 namedListeners[indexOfListener] = null;
26203 decrementListenerCount(self, 1, name);
26211 * @name $rootScope.Scope#$emit
26215 * Dispatches an event `name` upwards through the scope hierarchy notifying the
26216 * registered {@link ng.$rootScope.Scope#$on} listeners.
26218 * The event life cycle starts at the scope on which `$emit` was called. All
26219 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
26220 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
26221 * registered listeners along the way. The event will stop propagating if one of the listeners
26224 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
26225 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
26227 * @param {string} name Event name to emit.
26228 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
26229 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
26231 $emit: function(name, args) {
26235 stopPropagation = false,
26238 targetScope: scope,
26239 stopPropagation: function() {stopPropagation = true;},
26240 preventDefault: function() {
26241 event.defaultPrevented = true;
26243 defaultPrevented: false
26245 listenerArgs = concat([event], arguments, 1),
26249 namedListeners = scope.$$listeners[name] || empty;
26250 event.currentScope = scope;
26251 for (i = 0, length = namedListeners.length; i < length; i++) {
26253 // if listeners were deregistered, defragment the array
26254 if (!namedListeners[i]) {
26255 namedListeners.splice(i, 1);
26261 //allow all listeners attached to the current scope to run
26262 namedListeners[i].apply(null, listenerArgs);
26264 $exceptionHandler(e);
26267 //if any listener on the current scope stops propagation, prevent bubbling
26268 if (stopPropagation) {
26269 event.currentScope = null;
26273 scope = scope.$parent;
26276 event.currentScope = null;
26284 * @name $rootScope.Scope#$broadcast
26288 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
26289 * registered {@link ng.$rootScope.Scope#$on} listeners.
26291 * The event life cycle starts at the scope on which `$broadcast` was called. All
26292 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
26293 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
26294 * scope and calls all registered listeners along the way. The event cannot be canceled.
26296 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
26297 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
26299 * @param {string} name Event name to broadcast.
26300 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
26301 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
26303 $broadcast: function(name, args) {
26309 targetScope: target,
26310 preventDefault: function() {
26311 event.defaultPrevented = true;
26313 defaultPrevented: false
26316 if (!target.$$listenerCount[name]) return event;
26318 var listenerArgs = concat([event], arguments, 1),
26319 listeners, i, length;
26321 //down while you can, then up and next sibling or up and next sibling until back at root
26322 while ((current = next)) {
26323 event.currentScope = current;
26324 listeners = current.$$listeners[name] || [];
26325 for (i = 0, length = listeners.length; i < length; i++) {
26326 // if listeners were deregistered, defragment the array
26327 if (!listeners[i]) {
26328 listeners.splice(i, 1);
26335 listeners[i].apply(null, listenerArgs);
26337 $exceptionHandler(e);
26341 // Insanity Warning: scope depth-first traversal
26342 // yes, this code is a bit crazy, but it works and we have tests to prove it!
26343 // this piece should be kept in sync with the traversal in $digest
26344 // (though it differs due to having the extra check for $$listenerCount)
26345 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
26346 (current !== target && current.$$nextSibling)))) {
26347 while (current !== target && !(next = current.$$nextSibling)) {
26348 current = current.$parent;
26353 event.currentScope = null;
26358 var $rootScope = new Scope();
26360 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
26361 var asyncQueue = $rootScope.$$asyncQueue = [];
26362 var postDigestQueue = $rootScope.$$postDigestQueue = [];
26363 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
26368 function beginPhase(phase) {
26369 if ($rootScope.$$phase) {
26370 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
26373 $rootScope.$$phase = phase;
26376 function clearPhase() {
26377 $rootScope.$$phase = null;
26380 function incrementWatchersCount(current, count) {
26382 current.$$watchersCount += count;
26383 } while ((current = current.$parent));
26386 function decrementListenerCount(current, count, name) {
26388 current.$$listenerCount[name] -= count;
26390 if (current.$$listenerCount[name] === 0) {
26391 delete current.$$listenerCount[name];
26393 } while ((current = current.$parent));
26397 * function used as an initial value for watchers.
26398 * because it's unique we can easily tell it apart from other values
26400 function initWatchVal() {}
26402 function flushApplyAsync() {
26403 while (applyAsyncQueue.length) {
26405 applyAsyncQueue.shift()();
26407 $exceptionHandler(e);
26410 applyAsyncId = null;
26413 function scheduleApplyAsync() {
26414 if (applyAsyncId === null) {
26415 applyAsyncId = $browser.defer(function() {
26416 $rootScope.$apply(flushApplyAsync);
26425 * @name $rootElement
26428 * The root element of Angular application. This is either the element where {@link
26429 * ng.directive:ngApp ngApp} was declared or the element passed into
26430 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
26431 * location where the application's {@link auto.$injector $injector} service gets
26432 * published, and can be retrieved using `$rootElement.injector()`.
26436 // the implementation is in angular.bootstrap
26440 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
26442 function $$SanitizeUriProvider() {
26443 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
26444 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
26448 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
26449 * urls during a[href] sanitization.
26451 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
26453 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
26454 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
26455 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
26456 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
26458 * @param {RegExp=} regexp New regexp to whitelist urls with.
26459 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
26460 * chaining otherwise.
26462 this.aHrefSanitizationWhitelist = function(regexp) {
26463 if (isDefined(regexp)) {
26464 aHrefSanitizationWhitelist = regexp;
26467 return aHrefSanitizationWhitelist;
26473 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
26474 * urls during img[src] sanitization.
26476 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
26478 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
26479 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
26480 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
26481 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
26483 * @param {RegExp=} regexp New regexp to whitelist urls with.
26484 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
26485 * chaining otherwise.
26487 this.imgSrcSanitizationWhitelist = function(regexp) {
26488 if (isDefined(regexp)) {
26489 imgSrcSanitizationWhitelist = regexp;
26492 return imgSrcSanitizationWhitelist;
26495 this.$get = function() {
26496 return function sanitizeUri(uri, isImage) {
26497 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
26499 normalizedVal = urlResolve(uri).href;
26500 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
26501 return 'unsafe:' + normalizedVal;
26508 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26509 * Any commits to this file should be reviewed with security in mind. *
26510 * Changes to this file can potentially create security vulnerabilities. *
26511 * An approval from 2 Core members with history of modifying *
26512 * this file is required. *
26514 * Does the change somehow allow for arbitrary javascript to be executed? *
26515 * Or allows for someone to change the prototype of built-in objects? *
26516 * Or gives undesired access to variables likes document or window? *
26517 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26519 var $sceMinErr = minErr('$sce');
26521 var SCE_CONTEXTS = {
26525 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
26526 // url. (e.g. ng-include, script src, templateUrl)
26527 RESOURCE_URL: 'resourceUrl',
26531 // Helper functions follow.
26533 function adjustMatcher(matcher) {
26534 if (matcher === 'self') {
26536 } else if (isString(matcher)) {
26537 // Strings match exactly except for 2 wildcards - '*' and '**'.
26538 // '*' matches any character except those from the set ':/.?&'.
26539 // '**' matches any character (like .* in a RegExp).
26540 // More than 2 *'s raises an error as it's ill defined.
26541 if (matcher.indexOf('***') > -1) {
26542 throw $sceMinErr('iwcard',
26543 'Illegal sequence *** in string matcher. String: {0}', matcher);
26545 matcher = escapeForRegexp(matcher).
26546 replace('\\*\\*', '.*').
26547 replace('\\*', '[^:/.?&;]*');
26548 return new RegExp('^' + matcher + '$');
26549 } else if (isRegExp(matcher)) {
26550 // The only other type of matcher allowed is a Regexp.
26551 // Match entire URL / disallow partial matches.
26552 // Flags are reset (i.e. no global, ignoreCase or multiline)
26553 return new RegExp('^' + matcher.source + '$');
26555 throw $sceMinErr('imatcher',
26556 'Matchers may only be "self", string patterns or RegExp objects');
26561 function adjustMatchers(matchers) {
26562 var adjustedMatchers = [];
26563 if (isDefined(matchers)) {
26564 forEach(matchers, function(matcher) {
26565 adjustedMatchers.push(adjustMatcher(matcher));
26568 return adjustedMatchers;
26574 * @name $sceDelegate
26579 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
26580 * Contextual Escaping (SCE)} services to AngularJS.
26582 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
26583 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
26584 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
26585 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
26586 * work because `$sce` delegates to `$sceDelegate` for these operations.
26588 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
26590 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
26591 * can override it completely to change the behavior of `$sce`, the common case would
26592 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
26593 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
26594 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
26595 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
26596 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
26601 * @name $sceDelegateProvider
26604 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
26605 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
26606 * that the URLs used for sourcing Angular templates are safe. Refer {@link
26607 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
26608 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
26610 * For the general details about this service in Angular, read the main page for {@link ng.$sce
26611 * Strict Contextual Escaping (SCE)}.
26613 * **Example**: Consider the following case. <a name="example"></a>
26615 * - your app is hosted at url `http://myapp.example.com/`
26616 * - but some of your templates are hosted on other domains you control such as
26617 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
26618 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
26620 * Here is what a secure configuration for this scenario might look like:
26623 * angular.module('myApp', []).config(function($sceDelegateProvider) {
26624 * $sceDelegateProvider.resourceUrlWhitelist([
26625 * // Allow same origin resource loads.
26627 * // Allow loading from our assets domain. Notice the difference between * and **.
26628 * 'http://srv*.assets.example.com/**'
26631 * // The blacklist overrides the whitelist so the open redirect here is blocked.
26632 * $sceDelegateProvider.resourceUrlBlacklist([
26633 * 'http://myapp.example.com/clickThru**'
26639 function $SceDelegateProvider() {
26640 this.SCE_CONTEXTS = SCE_CONTEXTS;
26642 // Resource URLs can also be trusted by policy.
26643 var resourceUrlWhitelist = ['self'],
26644 resourceUrlBlacklist = [];
26648 * @name $sceDelegateProvider#resourceUrlWhitelist
26651 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
26652 * provided. This must be an array or null. A snapshot of this array is used so further
26653 * changes to the array are ignored.
26655 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
26656 * allowed in this array.
26658 * <div class="alert alert-warning">
26659 * **Note:** an empty whitelist array will block all URLs!
26662 * @return {Array} the currently set whitelist array.
26664 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
26665 * same origin resource requests.
26668 * Sets/Gets the whitelist of trusted resource URLs.
26670 this.resourceUrlWhitelist = function(value) {
26671 if (arguments.length) {
26672 resourceUrlWhitelist = adjustMatchers(value);
26674 return resourceUrlWhitelist;
26679 * @name $sceDelegateProvider#resourceUrlBlacklist
26682 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
26683 * provided. This must be an array or null. A snapshot of this array is used so further
26684 * changes to the array are ignored.
26686 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
26687 * allowed in this array.
26689 * The typical usage for the blacklist is to **block
26690 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
26691 * these would otherwise be trusted but actually return content from the redirected domain.
26693 * Finally, **the blacklist overrides the whitelist** and has the final say.
26695 * @return {Array} the currently set blacklist array.
26697 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
26698 * is no blacklist.)
26701 * Sets/Gets the blacklist of trusted resource URLs.
26704 this.resourceUrlBlacklist = function(value) {
26705 if (arguments.length) {
26706 resourceUrlBlacklist = adjustMatchers(value);
26708 return resourceUrlBlacklist;
26711 this.$get = ['$injector', function($injector) {
26713 var htmlSanitizer = function htmlSanitizer(html) {
26714 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
26717 if ($injector.has('$sanitize')) {
26718 htmlSanitizer = $injector.get('$sanitize');
26722 function matchUrl(matcher, parsedUrl) {
26723 if (matcher === 'self') {
26724 return urlIsSameOrigin(parsedUrl);
26726 // definitely a regex. See adjustMatchers()
26727 return !!matcher.exec(parsedUrl.href);
26731 function isResourceUrlAllowedByPolicy(url) {
26732 var parsedUrl = urlResolve(url.toString());
26733 var i, n, allowed = false;
26734 // Ensure that at least one item from the whitelist allows this url.
26735 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
26736 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
26742 // Ensure that no item from the blacklist blocked this url.
26743 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
26744 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
26753 function generateHolderType(Base) {
26754 var holderType = function TrustedValueHolderType(trustedValue) {
26755 this.$$unwrapTrustedValue = function() {
26756 return trustedValue;
26760 holderType.prototype = new Base();
26762 holderType.prototype.valueOf = function sceValueOf() {
26763 return this.$$unwrapTrustedValue();
26765 holderType.prototype.toString = function sceToString() {
26766 return this.$$unwrapTrustedValue().toString();
26771 var trustedValueHolderBase = generateHolderType(),
26774 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
26775 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
26776 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
26777 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
26778 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
26782 * @name $sceDelegate#trustAs
26785 * Returns an object that is trusted by angular for use in specified strict
26786 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
26787 * attribute interpolation, any dom event binding attribute interpolation
26788 * such as for onclick, etc.) that uses the provided value.
26789 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
26791 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
26792 * resourceUrl, html, js and css.
26793 * @param {*} value The value that that should be considered trusted/safe.
26794 * @returns {*} A value that can be used to stand in for the provided `value` in places
26795 * where Angular expects a $sce.trustAs() return value.
26797 function trustAs(type, trustedValue) {
26798 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
26799 if (!Constructor) {
26800 throw $sceMinErr('icontext',
26801 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
26802 type, trustedValue);
26804 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
26805 return trustedValue;
26807 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
26808 // mutable objects, we ensure here that the value passed in is actually a string.
26809 if (typeof trustedValue !== 'string') {
26810 throw $sceMinErr('itype',
26811 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
26814 return new Constructor(trustedValue);
26819 * @name $sceDelegate#valueOf
26822 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
26823 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
26824 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
26826 * If the passed parameter is not a value that had been returned by {@link
26827 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
26829 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
26830 * call or anything else.
26831 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
26832 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
26833 * `value` unchanged.
26835 function valueOf(maybeTrusted) {
26836 if (maybeTrusted instanceof trustedValueHolderBase) {
26837 return maybeTrusted.$$unwrapTrustedValue();
26839 return maybeTrusted;
26845 * @name $sceDelegate#getTrusted
26848 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
26849 * returns the originally supplied value if the queried context type is a supertype of the
26850 * created type. If this condition isn't satisfied, throws an exception.
26852 * <div class="alert alert-danger">
26853 * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
26854 * (XSS) vulnerability in your application.
26857 * @param {string} type The kind of context in which this value is to be used.
26858 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
26859 * `$sceDelegate.trustAs`} call.
26860 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
26861 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
26863 function getTrusted(type, maybeTrusted) {
26864 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
26865 return maybeTrusted;
26867 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
26868 if (constructor && maybeTrusted instanceof constructor) {
26869 return maybeTrusted.$$unwrapTrustedValue();
26871 // If we get here, then we may only take one of two actions.
26872 // 1. sanitize the value for the requested type, or
26873 // 2. throw an exception.
26874 if (type === SCE_CONTEXTS.RESOURCE_URL) {
26875 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
26876 return maybeTrusted;
26878 throw $sceMinErr('insecurl',
26879 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
26880 maybeTrusted.toString());
26882 } else if (type === SCE_CONTEXTS.HTML) {
26883 return htmlSanitizer(maybeTrusted);
26885 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
26888 return { trustAs: trustAs,
26889 getTrusted: getTrusted,
26890 valueOf: valueOf };
26897 * @name $sceProvider
26900 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
26901 * - enable/disable Strict Contextual Escaping (SCE) in a module
26902 * - override the default implementation with a custom delegate
26904 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
26907 /* jshint maxlen: false*/
26916 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
26918 * # Strict Contextual Escaping
26920 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
26921 * contexts to result in a value that is marked as safe to use for that context. One example of
26922 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
26923 * to these contexts as privileged or SCE contexts.
26925 * As of version 1.2, Angular ships with SCE enabled by default.
26927 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
26928 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
26929 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
26930 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
26931 * to the top of your HTML document.
26933 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
26934 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
26936 * Here's an example of a binding in a privileged context:
26939 * <input ng-model="userHtml" aria-label="User input">
26940 * <div ng-bind-html="userHtml"></div>
26943 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
26944 * disabled, this application allows the user to render arbitrary HTML into the DIV.
26945 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
26946 * bindings. (HTML is just one example of a context where rendering user controlled input creates
26947 * security vulnerabilities.)
26949 * For the case of HTML, you might use a library, either on the client side, or on the server side,
26950 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
26952 * How would you ensure that every place that used these types of bindings was bound to a value that
26953 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
26954 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
26955 * properties/fields and forgot to update the binding to the sanitized value?
26957 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
26958 * determine that something explicitly says it's safe to use a value for binding in that
26959 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
26960 * for those values that you can easily tell are safe - because they were received from your server,
26961 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
26962 * allowing only the files in a specific directory to do this. Ensuring that the internal API
26963 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
26965 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
26966 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
26967 * obtain values that will be accepted by SCE / privileged contexts.
26970 * ## How does it work?
26972 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
26973 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
26974 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
26975 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
26977 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
26978 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
26982 * var ngBindHtmlDirective = ['$sce', function($sce) {
26983 * return function(scope, element, attr) {
26984 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
26985 * element.html(value || '');
26991 * ## Impact on loading templates
26993 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
26994 * `templateUrl`'s specified by {@link guide/directive directives}.
26996 * By default, Angular only loads templates from the same domain and protocol as the application
26997 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
26998 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
26999 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
27000 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
27004 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
27005 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
27006 * policy apply in addition to this and may further restrict whether the template is successfully
27007 * loaded. This means that without the right CORS policy, loading templates from a different domain
27008 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
27011 * ## This feels like too much overhead
27013 * It's important to remember that SCE only applies to interpolation expressions.
27015 * If your expressions are constant literals, they're automatically trusted and you don't need to
27016 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
27017 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
27019 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
27020 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
27022 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
27023 * templates in `ng-include` from your application's domain without having to even know about SCE.
27024 * It blocks loading templates from other domains or loading templates over http from an https
27025 * served document. You can change these by setting your own custom {@link
27026 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
27027 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
27029 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
27030 * application that's secure and can be audited to verify that with much more ease than bolting
27031 * security onto an application later.
27033 * <a name="contexts"></a>
27034 * ## What trusted context types are supported?
27036 * | Context | Notes |
27037 * |---------------------|----------------|
27038 * | `$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. |
27039 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
27040 * | `$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. |
27041 * | `$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. |
27042 * | `$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. |
27044 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
27046 * Each element in these arrays must be one of the following:
27049 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
27050 * domain** as the application document using the **same protocol**.
27051 * - **String** (except the special value `'self'`)
27052 * - The string is matched against the full *normalized / absolute URL* of the resource
27053 * being tested (substring matches are not good enough.)
27054 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
27055 * match themselves.
27056 * - `*`: matches zero or more occurrences of any character other than one of the following 6
27057 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
27059 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
27060 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
27061 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
27062 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
27063 * http://foo.example.com/templates/**).
27064 * - **RegExp** (*see caveat below*)
27065 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
27066 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
27067 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
27068 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
27069 * small number of cases. A `.` character in the regex used when matching the scheme or a
27070 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
27071 * is highly recommended to use the string patterns and only fall back to regular expressions
27072 * as a last resort.
27073 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
27074 * matched against the **entire** *normalized / absolute URL* of the resource being tested
27075 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
27076 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
27077 * - If you are generating your JavaScript from some other templating engine (not
27078 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
27079 * remember to escape your regular expression (and be aware that you might need more than
27080 * one level of escaping depending on your templating engine and the way you interpolated
27081 * the value.) Do make use of your platform's escaping mechanism as it might be good
27082 * enough before coding your own. E.g. Ruby has
27083 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
27084 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
27085 * Javascript lacks a similar built in function for escaping. Take a look at Google
27086 * Closure library's [goog.string.regExpEscape(s)](
27087 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
27089 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
27091 * ## Show me an example using SCE.
27093 * <example module="mySceApp" deps="angular-sanitize.js">
27094 * <file name="index.html">
27095 * <div ng-controller="AppController as myCtrl">
27096 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
27097 * <b>User comments</b><br>
27098 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
27099 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
27101 * <div class="well">
27102 * <div ng-repeat="userComment in myCtrl.userComments">
27103 * <b>{{userComment.name}}</b>:
27104 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
27111 * <file name="script.js">
27112 * angular.module('mySceApp', ['ngSanitize'])
27113 * .controller('AppController', ['$http', '$templateCache', '$sce',
27114 * function($http, $templateCache, $sce) {
27116 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
27117 * self.userComments = userComments;
27119 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
27120 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
27121 * 'sanitization."">Hover over this text.</span>');
27125 * <file name="test_data.json">
27127 * { "name": "Alice",
27129 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
27132 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
27137 * <file name="protractor.js" type="protractor">
27138 * describe('SCE doc demo', function() {
27139 * it('should sanitize untrusted values', function() {
27140 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
27141 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
27144 * it('should NOT sanitize explicitly trusted values', function() {
27145 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
27146 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
27147 * 'sanitization."">Hover over this text.</span>');
27155 * ## Can I disable SCE completely?
27157 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
27158 * for little coding overhead. It will be much harder to take an SCE disabled application and
27159 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
27160 * for cases where you have a lot of existing code that was written before SCE was introduced and
27161 * you're migrating them a module at a time.
27163 * That said, here's how you can completely disable SCE:
27166 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
27167 * // Completely disable SCE. For demonstration purposes only!
27168 * // Do not use in new projects.
27169 * $sceProvider.enabled(false);
27174 /* jshint maxlen: 100 */
27176 function $SceProvider() {
27177 var enabled = true;
27181 * @name $sceProvider#enabled
27184 * @param {boolean=} value If provided, then enables/disables SCE.
27185 * @return {boolean} true if SCE is enabled, false otherwise.
27188 * Enables/disables SCE and returns the current value.
27190 this.enabled = function(value) {
27191 if (arguments.length) {
27198 /* Design notes on the default implementation for SCE.
27200 * The API contract for the SCE delegate
27201 * -------------------------------------
27202 * The SCE delegate object must provide the following 3 methods:
27204 * - trustAs(contextEnum, value)
27205 * This method is used to tell the SCE service that the provided value is OK to use in the
27206 * contexts specified by contextEnum. It must return an object that will be accepted by
27207 * getTrusted() for a compatible contextEnum and return this value.
27210 * For values that were not produced by trustAs(), return them as is. For values that were
27211 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
27212 * trustAs is wrapping the given values into some type, this operation unwraps it when given
27215 * - getTrusted(contextEnum, value)
27216 * This function should return the a value that is safe to use in the context specified by
27217 * contextEnum or throw and exception otherwise.
27219 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
27220 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
27221 * instance, an implementation could maintain a registry of all trusted objects by context. In
27222 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
27223 * return the same object passed in if it was found in the registry under a compatible context or
27224 * throw an exception otherwise. An implementation might only wrap values some of the time based
27225 * on some criteria. getTrusted() might return a value and not throw an exception for special
27226 * constants or objects even if not wrapped. All such implementations fulfill this contract.
27229 * A note on the inheritance model for SCE contexts
27230 * ------------------------------------------------
27231 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
27232 * is purely an implementation details.
27234 * The contract is simply this:
27236 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
27237 * will also succeed.
27239 * Inheritance happens to capture this in a natural way. In some future, we
27240 * may not use inheritance anymore. That is OK because no code outside of
27241 * sce.js and sceSpecs.js would need to be aware of this detail.
27244 this.$get = ['$parse', '$sceDelegate', function(
27245 $parse, $sceDelegate) {
27246 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
27247 // the "expression(javascript expression)" syntax which is insecure.
27248 if (enabled && msie < 8) {
27249 throw $sceMinErr('iequirks',
27250 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
27251 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
27252 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
27255 var sce = shallowCopy(SCE_CONTEXTS);
27259 * @name $sce#isEnabled
27262 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
27263 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
27266 * Returns a boolean indicating if SCE is enabled.
27268 sce.isEnabled = function() {
27271 sce.trustAs = $sceDelegate.trustAs;
27272 sce.getTrusted = $sceDelegate.getTrusted;
27273 sce.valueOf = $sceDelegate.valueOf;
27276 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
27277 sce.valueOf = identity;
27282 * @name $sce#parseAs
27285 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
27286 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
27287 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
27290 * @param {string} type The kind of SCE context in which this result will be used.
27291 * @param {string} expression String expression to compile.
27292 * @returns {function(context, locals)} a function which represents the compiled expression:
27294 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27295 * are evaluated against (typically a scope object).
27296 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27299 sce.parseAs = function sceParseAs(type, expr) {
27300 var parsed = $parse(expr);
27301 if (parsed.literal && parsed.constant) {
27304 return $parse(expr, function(value) {
27305 return sce.getTrusted(type, value);
27312 * @name $sce#trustAs
27315 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
27316 * returns an object that is trusted by angular for use in specified strict contextual
27317 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
27318 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
27319 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
27322 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
27323 * resourceUrl, html, js and css.
27324 * @param {*} value The value that that should be considered trusted/safe.
27325 * @returns {*} A value that can be used to stand in for the provided `value` in places
27326 * where Angular expects a $sce.trustAs() return value.
27331 * @name $sce#trustAsHtml
27334 * Shorthand method. `$sce.trustAsHtml(value)` →
27335 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
27337 * @param {*} value The value to trustAs.
27338 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
27339 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
27340 * only accept expressions that are either literal constants or are the
27341 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
27346 * @name $sce#trustAsUrl
27349 * Shorthand method. `$sce.trustAsUrl(value)` →
27350 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
27352 * @param {*} value The value to trustAs.
27353 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
27354 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
27355 * only accept expressions that are either literal constants or are the
27356 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
27361 * @name $sce#trustAsResourceUrl
27364 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
27365 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
27367 * @param {*} value The value to trustAs.
27368 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
27369 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
27370 * only accept expressions that are either literal constants or are the return
27371 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
27376 * @name $sce#trustAsJs
27379 * Shorthand method. `$sce.trustAsJs(value)` →
27380 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
27382 * @param {*} value The value to trustAs.
27383 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
27384 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
27385 * only accept expressions that are either literal constants or are the
27386 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
27391 * @name $sce#getTrusted
27394 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
27395 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
27396 * originally supplied value if the queried context type is a supertype of the created type.
27397 * If this condition isn't satisfied, throws an exception.
27399 * @param {string} type The kind of context in which this value is to be used.
27400 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
27402 * @returns {*} The value the was originally provided to
27403 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
27404 * Otherwise, throws an exception.
27409 * @name $sce#getTrustedHtml
27412 * Shorthand method. `$sce.getTrustedHtml(value)` →
27413 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
27415 * @param {*} value The value to pass to `$sce.getTrusted`.
27416 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
27421 * @name $sce#getTrustedCss
27424 * Shorthand method. `$sce.getTrustedCss(value)` →
27425 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
27427 * @param {*} value The value to pass to `$sce.getTrusted`.
27428 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
27433 * @name $sce#getTrustedUrl
27436 * Shorthand method. `$sce.getTrustedUrl(value)` →
27437 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
27439 * @param {*} value The value to pass to `$sce.getTrusted`.
27440 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
27445 * @name $sce#getTrustedResourceUrl
27448 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
27449 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
27451 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
27452 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
27457 * @name $sce#getTrustedJs
27460 * Shorthand method. `$sce.getTrustedJs(value)` →
27461 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
27463 * @param {*} value The value to pass to `$sce.getTrusted`.
27464 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
27469 * @name $sce#parseAsHtml
27472 * Shorthand method. `$sce.parseAsHtml(expression string)` →
27473 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
27475 * @param {string} expression String expression to compile.
27476 * @returns {function(context, locals)} a function which represents the compiled expression:
27478 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27479 * are evaluated against (typically a scope object).
27480 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27486 * @name $sce#parseAsCss
27489 * Shorthand method. `$sce.parseAsCss(value)` →
27490 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
27492 * @param {string} expression String expression to compile.
27493 * @returns {function(context, locals)} a function which represents the compiled expression:
27495 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27496 * are evaluated against (typically a scope object).
27497 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27503 * @name $sce#parseAsUrl
27506 * Shorthand method. `$sce.parseAsUrl(value)` →
27507 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
27509 * @param {string} expression String expression to compile.
27510 * @returns {function(context, locals)} a function which represents the compiled expression:
27512 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27513 * are evaluated against (typically a scope object).
27514 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27520 * @name $sce#parseAsResourceUrl
27523 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
27524 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
27526 * @param {string} expression String expression to compile.
27527 * @returns {function(context, locals)} a function which represents the compiled expression:
27529 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27530 * are evaluated against (typically a scope object).
27531 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27537 * @name $sce#parseAsJs
27540 * Shorthand method. `$sce.parseAsJs(value)` →
27541 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
27543 * @param {string} expression String expression to compile.
27544 * @returns {function(context, locals)} a function which represents the compiled expression:
27546 * * `context` – `{object}` – an object against which any expressions embedded in the strings
27547 * are evaluated against (typically a scope object).
27548 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
27552 // Shorthand delegations.
27553 var parse = sce.parseAs,
27554 getTrusted = sce.getTrusted,
27555 trustAs = sce.trustAs;
27557 forEach(SCE_CONTEXTS, function(enumValue, name) {
27558 var lName = lowercase(name);
27559 sce[camelCase("parse_as_" + lName)] = function(expr) {
27560 return parse(enumValue, expr);
27562 sce[camelCase("get_trusted_" + lName)] = function(value) {
27563 return getTrusted(enumValue, value);
27565 sce[camelCase("trust_as_" + lName)] = function(value) {
27566 return trustAs(enumValue, value);
27575 * !!! This is an undocumented "private" service !!!
27578 * @requires $window
27579 * @requires $document
27581 * @property {boolean} history Does the browser support html5 history api ?
27582 * @property {boolean} transitions Does the browser support CSS transition events ?
27583 * @property {boolean} animations Does the browser support CSS animation events ?
27586 * This is very simple implementation of testing browser's features.
27588 function $SnifferProvider() {
27589 this.$get = ['$window', '$document', function($window, $document) {
27590 var eventSupport = {},
27592 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
27593 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
27594 document = $document[0] || {},
27596 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
27597 bodyStyle = document.body && document.body.style,
27598 transitions = false,
27599 animations = false,
27603 for (var prop in bodyStyle) {
27604 if (match = vendorRegex.exec(prop)) {
27605 vendorPrefix = match[0];
27606 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
27611 if (!vendorPrefix) {
27612 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
27615 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
27616 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
27618 if (android && (!transitions || !animations)) {
27619 transitions = isString(bodyStyle.webkitTransition);
27620 animations = isString(bodyStyle.webkitAnimation);
27626 // Android has history.pushState, but it does not update location correctly
27627 // so let's not use the history API at all.
27628 // http://code.google.com/p/android/issues/detail?id=17471
27629 // https://github.com/angular/angular.js/issues/904
27631 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
27632 // so let's not use the history API also
27633 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
27635 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
27637 hasEvent: function(event) {
27638 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
27639 // it. In particular the event is not fired when backspace or delete key are pressed or
27640 // when cut operation is performed.
27641 // IE10+ implements 'input' event but it erroneously fires under various situations,
27642 // e.g. when placeholder changes, or a form is focused.
27643 if (event === 'input' && msie <= 11) return false;
27645 if (isUndefined(eventSupport[event])) {
27646 var divElm = document.createElement('div');
27647 eventSupport[event] = 'on' + event in divElm;
27650 return eventSupport[event];
27653 vendorPrefix: vendorPrefix,
27654 transitions: transitions,
27655 animations: animations,
27661 var $compileMinErr = minErr('$compile');
27665 * @name $templateRequestProvider
27667 * Used to configure the options passed to the {@link $http} service when making a template request.
27669 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
27670 * requesting a template.
27672 function $TemplateRequestProvider() {
27678 * @name $templateRequestProvider#httpOptions
27680 * The options to be passed to the {@link $http} service when making the request.
27681 * You can use this to override options such as the "Accept" header for template requests.
27683 * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
27684 * options if not overridden here.
27686 * @param {string=} value new value for the {@link $http} options.
27687 * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
27689 this.httpOptions = function(val) {
27694 return httpOptions;
27699 * @name $templateRequest
27702 * The `$templateRequest` service runs security checks then downloads the provided template using
27703 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
27704 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
27705 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
27706 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
27707 * when `tpl` is of type string and `$templateCache` has the matching entry.
27709 * If you want to pass custom options to the `$http` service, such as setting the Accept header you
27710 * can configure this via {@link $templateRequestProvider#httpOptions}.
27712 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
27713 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
27715 * @return {Promise} a promise for the HTTP response data of the given URL.
27717 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
27719 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
27721 function handleRequestFn(tpl, ignoreRequestError) {
27722 handleRequestFn.totalPendingRequests++;
27724 // We consider the template cache holds only trusted templates, so
27725 // there's no need to go through whitelisting again for keys that already
27726 // are included in there. This also makes Angular accept any script
27727 // directive, no matter its name. However, we still need to unwrap trusted
27729 if (!isString(tpl) || !$templateCache.get(tpl)) {
27730 tpl = $sce.getTrustedResourceUrl(tpl);
27733 var transformResponse = $http.defaults && $http.defaults.transformResponse;
27735 if (isArray(transformResponse)) {
27736 transformResponse = transformResponse.filter(function(transformer) {
27737 return transformer !== defaultHttpResponseTransform;
27739 } else if (transformResponse === defaultHttpResponseTransform) {
27740 transformResponse = null;
27743 return $http.get(tpl, extend({
27744 cache: $templateCache,
27745 transformResponse: transformResponse
27747 ['finally'](function() {
27748 handleRequestFn.totalPendingRequests--;
27750 .then(function(response) {
27751 $templateCache.put(tpl, response.data);
27752 return response.data;
27755 function handleError(resp) {
27756 if (!ignoreRequestError) {
27757 throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
27758 tpl, resp.status, resp.statusText);
27760 return $q.reject(resp);
27764 handleRequestFn.totalPendingRequests = 0;
27766 return handleRequestFn;
27770 function $$TestabilityProvider() {
27771 this.$get = ['$rootScope', '$browser', '$location',
27772 function($rootScope, $browser, $location) {
27775 * @name $testability
27778 * The private $$testability service provides a collection of methods for use when debugging
27779 * or by automated test and debugging tools.
27781 var testability = {};
27784 * @name $$testability#findBindings
27787 * Returns an array of elements that are bound (via ng-bind or {{}})
27788 * to expressions matching the input.
27790 * @param {Element} element The element root to search from.
27791 * @param {string} expression The binding expression to match.
27792 * @param {boolean} opt_exactMatch If true, only returns exact matches
27793 * for the expression. Filters and whitespace are ignored.
27795 testability.findBindings = function(element, expression, opt_exactMatch) {
27796 var bindings = element.getElementsByClassName('ng-binding');
27798 forEach(bindings, function(binding) {
27799 var dataBinding = angular.element(binding).data('$binding');
27801 forEach(dataBinding, function(bindingName) {
27802 if (opt_exactMatch) {
27803 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
27804 if (matcher.test(bindingName)) {
27805 matches.push(binding);
27808 if (bindingName.indexOf(expression) != -1) {
27809 matches.push(binding);
27819 * @name $$testability#findModels
27822 * Returns an array of elements that are two-way found via ng-model to
27823 * expressions matching the input.
27825 * @param {Element} element The element root to search from.
27826 * @param {string} expression The model expression to match.
27827 * @param {boolean} opt_exactMatch If true, only returns exact matches
27828 * for the expression.
27830 testability.findModels = function(element, expression, opt_exactMatch) {
27831 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
27832 for (var p = 0; p < prefixes.length; ++p) {
27833 var attributeEquals = opt_exactMatch ? '=' : '*=';
27834 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
27835 var elements = element.querySelectorAll(selector);
27836 if (elements.length) {
27843 * @name $$testability#getLocation
27846 * Shortcut for getting the location in a browser agnostic way. Returns
27847 * the path, search, and hash. (e.g. /path?a=b#hash)
27849 testability.getLocation = function() {
27850 return $location.url();
27854 * @name $$testability#setLocation
27857 * Shortcut for navigating to a location without doing a full page reload.
27859 * @param {string} url The location url (path, search and hash,
27860 * e.g. /path?a=b#hash) to go to.
27862 testability.setLocation = function(url) {
27863 if (url !== $location.url()) {
27864 $location.url(url);
27865 $rootScope.$digest();
27870 * @name $$testability#whenStable
27873 * Calls the callback when $timeout and $http requests are completed.
27875 * @param {function} callback
27877 testability.whenStable = function(callback) {
27878 $browser.notifyWhenNoOutstandingRequests(callback);
27881 return testability;
27885 function $TimeoutProvider() {
27886 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
27887 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
27889 var deferreds = {};
27897 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
27898 * block and delegates any exceptions to
27899 * {@link ng.$exceptionHandler $exceptionHandler} service.
27901 * The return value of calling `$timeout` is a promise, which will be resolved when
27902 * the delay has passed and the timeout function, if provided, is executed.
27904 * To cancel a timeout request, call `$timeout.cancel(promise)`.
27906 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
27907 * synchronously flush the queue of deferred functions.
27909 * If you only want a promise that will be resolved after some specified delay
27910 * then you can call `$timeout` without the `fn` function.
27912 * @param {function()=} fn A function, whose execution should be delayed.
27913 * @param {number=} [delay=0] Delay in milliseconds.
27914 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
27915 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
27916 * @param {...*=} Pass additional parameters to the executed function.
27917 * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
27918 * will be resolved with the return value of the `fn` function.
27921 function timeout(fn, delay, invokeApply) {
27922 if (!isFunction(fn)) {
27923 invokeApply = delay;
27928 var args = sliceArgs(arguments, 3),
27929 skipApply = (isDefined(invokeApply) && !invokeApply),
27930 deferred = (skipApply ? $$q : $q).defer(),
27931 promise = deferred.promise,
27934 timeoutId = $browser.defer(function() {
27936 deferred.resolve(fn.apply(null, args));
27938 deferred.reject(e);
27939 $exceptionHandler(e);
27942 delete deferreds[promise.$$timeoutId];
27945 if (!skipApply) $rootScope.$apply();
27948 promise.$$timeoutId = timeoutId;
27949 deferreds[timeoutId] = deferred;
27957 * @name $timeout#cancel
27960 * Cancels a task associated with the `promise`. As a result of this, the promise will be
27961 * resolved with a rejection.
27963 * @param {Promise=} promise Promise returned by the `$timeout` function.
27964 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
27967 timeout.cancel = function(promise) {
27968 if (promise && promise.$$timeoutId in deferreds) {
27969 deferreds[promise.$$timeoutId].reject('canceled');
27970 delete deferreds[promise.$$timeoutId];
27971 return $browser.defer.cancel(promise.$$timeoutId);
27980 // NOTE: The usage of window and document instead of $window and $document here is
27981 // deliberate. This service depends on the specific behavior of anchor nodes created by the
27982 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
27983 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
27984 // doesn't know about mocked locations and resolves URLs to the real document - which is
27985 // exactly the behavior needed here. There is little value is mocking these out for this
27987 var urlParsingNode = document.createElement("a");
27988 var originUrl = urlResolve(window.location.href);
27993 * Implementation Notes for non-IE browsers
27994 * ----------------------------------------
27995 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
27996 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
27997 * URL will be resolved into an absolute URL in the context of the application document.
27998 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
27999 * properties are all populated to reflect the normalized URL. This approach has wide
28000 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
28001 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
28003 * Implementation Notes for IE
28004 * ---------------------------
28005 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
28006 * browsers. However, the parsed components will not be set if the URL assigned did not specify
28007 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
28008 * work around that by performing the parsing in a 2nd step by taking a previously normalized
28009 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
28010 * properties such as protocol, hostname, port, etc.
28013 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
28014 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
28015 * http://url.spec.whatwg.org/#urlutils
28016 * https://github.com/angular/angular.js/pull/2902
28017 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
28020 * @param {string} url The URL to be parsed.
28021 * @description Normalizes and parses a URL.
28022 * @returns {object} Returns the normalized URL as a dictionary.
28024 * | member name | Description |
28025 * |---------------|----------------|
28026 * | href | A normalized version of the provided URL if it was not an absolute URL |
28027 * | protocol | The protocol including the trailing colon |
28028 * | host | The host and port (if the port is non-default) of the normalizedUrl |
28029 * | search | The search params, minus the question mark |
28030 * | hash | The hash string, minus the hash symbol
28031 * | hostname | The hostname
28032 * | port | The port, without ":"
28033 * | pathname | The pathname, beginning with "/"
28036 function urlResolve(url) {
28040 // Normalize before parse. Refer Implementation Notes on why this is
28041 // done in two steps on IE.
28042 urlParsingNode.setAttribute("href", href);
28043 href = urlParsingNode.href;
28046 urlParsingNode.setAttribute('href', href);
28048 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
28050 href: urlParsingNode.href,
28051 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
28052 host: urlParsingNode.host,
28053 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
28054 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
28055 hostname: urlParsingNode.hostname,
28056 port: urlParsingNode.port,
28057 pathname: (urlParsingNode.pathname.charAt(0) === '/')
28058 ? urlParsingNode.pathname
28059 : '/' + urlParsingNode.pathname
28064 * Parse a request URL and determine whether this is a same-origin request as the application document.
28066 * @param {string|object} requestUrl The url of the request as a string that will be resolved
28067 * or a parsed URL object.
28068 * @returns {boolean} Whether the request is for the same origin as the application document.
28070 function urlIsSameOrigin(requestUrl) {
28071 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
28072 return (parsed.protocol === originUrl.protocol &&
28073 parsed.host === originUrl.host);
28081 * A reference to the browser's `window` object. While `window`
28082 * is globally available in JavaScript, it causes testability problems, because
28083 * it is a global variable. In angular we always refer to it through the
28084 * `$window` service, so it may be overridden, removed or mocked for testing.
28086 * Expressions, like the one defined for the `ngClick` directive in the example
28087 * below, are evaluated with respect to the current scope. Therefore, there is
28088 * no risk of inadvertently coding in a dependency on a global value in such an
28092 <example module="windowExample">
28093 <file name="index.html">
28095 angular.module('windowExample', [])
28096 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
28097 $scope.greeting = 'Hello, World!';
28098 $scope.doGreeting = function(greeting) {
28099 $window.alert(greeting);
28103 <div ng-controller="ExampleController">
28104 <input type="text" ng-model="greeting" aria-label="greeting" />
28105 <button ng-click="doGreeting(greeting)">ALERT</button>
28108 <file name="protractor.js" type="protractor">
28109 it('should display the greeting in the input box', function() {
28110 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
28111 // If we click the button it will block the test runner
28112 // element(':button').click();
28117 function $WindowProvider() {
28118 this.$get = valueFn(window);
28122 * @name $$cookieReader
28123 * @requires $document
28126 * This is a private service for reading cookies used by $http and ngCookies
28128 * @return {Object} a key/value map of the current cookies
28130 function $$CookieReader($document) {
28131 var rawDocument = $document[0] || {};
28132 var lastCookies = {};
28133 var lastCookieString = '';
28135 function safeDecodeURIComponent(str) {
28137 return decodeURIComponent(str);
28143 return function() {
28144 var cookieArray, cookie, i, index, name;
28145 var currentCookieString = rawDocument.cookie || '';
28147 if (currentCookieString !== lastCookieString) {
28148 lastCookieString = currentCookieString;
28149 cookieArray = lastCookieString.split('; ');
28152 for (i = 0; i < cookieArray.length; i++) {
28153 cookie = cookieArray[i];
28154 index = cookie.indexOf('=');
28155 if (index > 0) { //ignore nameless cookies
28156 name = safeDecodeURIComponent(cookie.substring(0, index));
28157 // the first value that is seen for a cookie is the most
28158 // specific one. values for the same cookie name that
28159 // follow are for less specific paths.
28160 if (isUndefined(lastCookies[name])) {
28161 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
28166 return lastCookies;
28170 $$CookieReader.$inject = ['$document'];
28172 function $$CookieReaderProvider() {
28173 this.$get = $$CookieReader;
28176 /* global currencyFilter: true,
28178 filterFilter: true,
28180 limitToFilter: true,
28181 lowercaseFilter: true,
28182 numberFilter: true,
28183 orderByFilter: true,
28184 uppercaseFilter: true,
28189 * @name $filterProvider
28192 * Filters are just functions which transform input to an output. However filters need to be
28193 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
28194 * annotated with dependencies and is responsible for creating a filter function.
28196 * <div class="alert alert-warning">
28197 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
28198 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
28199 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
28200 * (`myapp_subsection_filterx`).
28204 * // Filter registration
28205 * function MyModule($provide, $filterProvider) {
28206 * // create a service to demonstrate injection (not always needed)
28207 * $provide.value('greet', function(name){
28208 * return 'Hello ' + name + '!';
28211 * // register a filter factory which uses the
28212 * // greet service to demonstrate DI.
28213 * $filterProvider.register('greet', function(greet){
28214 * // return the filter function which uses the greet service
28215 * // to generate salutation
28216 * return function(text) {
28217 * // filters need to be forgiving so check input validity
28218 * return text && greet(text) || text;
28224 * The filter function is registered with the `$injector` under the filter name suffix with
28228 * it('should be the same instance', inject(
28229 * function($filterProvider) {
28230 * $filterProvider.register('reverse', function(){
28234 * function($filter, reverseFilter) {
28235 * expect($filter('reverse')).toBe(reverseFilter);
28240 * For more information about how angular filters work, and how to create your own filters, see
28241 * {@link guide/filter Filters} in the Angular Developer Guide.
28249 * Filters are used for formatting data displayed to the user.
28251 * The general syntax in templates is as follows:
28253 * {{ expression [| filter_name[:parameter_value] ... ] }}
28255 * @param {String} name Name of the filter function to retrieve
28256 * @return {Function} the filter function
28258 <example name="$filter" module="filterExample">
28259 <file name="index.html">
28260 <div ng-controller="MainCtrl">
28261 <h3>{{ originalText }}</h3>
28262 <h3>{{ filteredText }}</h3>
28266 <file name="script.js">
28267 angular.module('filterExample', [])
28268 .controller('MainCtrl', function($scope, $filter) {
28269 $scope.originalText = 'hello';
28270 $scope.filteredText = $filter('uppercase')($scope.originalText);
28275 $FilterProvider.$inject = ['$provide'];
28276 function $FilterProvider($provide) {
28277 var suffix = 'Filter';
28281 * @name $filterProvider#register
28282 * @param {string|Object} name Name of the filter function, or an object map of filters where
28283 * the keys are the filter names and the values are the filter factories.
28285 * <div class="alert alert-warning">
28286 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
28287 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
28288 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
28289 * (`myapp_subsection_filterx`).
28291 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
28292 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
28293 * of the registered filter instances.
28295 function register(name, factory) {
28296 if (isObject(name)) {
28298 forEach(name, function(filter, key) {
28299 filters[key] = register(key, filter);
28303 return $provide.factory(name + suffix, factory);
28306 this.register = register;
28308 this.$get = ['$injector', function($injector) {
28309 return function(name) {
28310 return $injector.get(name + suffix);
28314 ////////////////////////////////////////
28317 currencyFilter: false,
28319 filterFilter: false,
28321 limitToFilter: false,
28322 lowercaseFilter: false,
28323 numberFilter: false,
28324 orderByFilter: false,
28325 uppercaseFilter: false,
28328 register('currency', currencyFilter);
28329 register('date', dateFilter);
28330 register('filter', filterFilter);
28331 register('json', jsonFilter);
28332 register('limitTo', limitToFilter);
28333 register('lowercase', lowercaseFilter);
28334 register('number', numberFilter);
28335 register('orderBy', orderByFilter);
28336 register('uppercase', uppercaseFilter);
28345 * Selects a subset of items from `array` and returns it as a new array.
28347 * @param {Array} array The source array.
28348 * @param {string|Object|function()} expression The predicate to be used for selecting items from
28353 * - `string`: The string is used for matching against the contents of the `array`. All strings or
28354 * objects with string properties in `array` that match this string will be returned. This also
28355 * applies to nested object properties.
28356 * The predicate can be negated by prefixing the string with `!`.
28358 * - `Object`: A pattern object can be used to filter specific properties on objects contained
28359 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
28360 * which have property `name` containing "M" and property `phone` containing "1". A special
28361 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
28362 * property of the object or its nested object properties. That's equivalent to the simple
28363 * substring match with a `string` as described above. The predicate can be negated by prefixing
28364 * the string with `!`.
28365 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
28366 * not containing "M".
28368 * Note that a named property will match properties on the same level only, while the special
28369 * `$` property will match properties on the same level or deeper. E.g. an array item like
28370 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
28371 * **will** be matched by `{$: 'John'}`.
28373 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
28374 * The function is called for each element of the array, with the element, its index, and
28375 * the entire array itself as arguments.
28377 * The final result is an array of those elements that the predicate returned true for.
28379 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
28380 * determining if the expected value (from the filter expression) and actual value (from
28381 * the object in the array) should be considered a match.
28385 * - `function(actual, expected)`:
28386 * The function will be given the object value and the predicate value to compare and
28387 * should return true if both values should be considered equal.
28389 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
28390 * This is essentially strict comparison of expected and actual.
28392 * - `false|undefined`: A short hand for a function which will look for a substring match in case
28395 * Primitive values are converted to strings. Objects are not compared against primitives,
28396 * unless they have a custom `toString` method (e.g. `Date` objects).
28400 <file name="index.html">
28401 <div ng-init="friends = [{name:'John', phone:'555-1276'},
28402 {name:'Mary', phone:'800-BIG-MARY'},
28403 {name:'Mike', phone:'555-4321'},
28404 {name:'Adam', phone:'555-5678'},
28405 {name:'Julie', phone:'555-8765'},
28406 {name:'Juliette', phone:'555-5678'}]"></div>
28408 <label>Search: <input ng-model="searchText"></label>
28409 <table id="searchTextResults">
28410 <tr><th>Name</th><th>Phone</th></tr>
28411 <tr ng-repeat="friend in friends | filter:searchText">
28412 <td>{{friend.name}}</td>
28413 <td>{{friend.phone}}</td>
28417 <label>Any: <input ng-model="search.$"></label> <br>
28418 <label>Name only <input ng-model="search.name"></label><br>
28419 <label>Phone only <input ng-model="search.phone"></label><br>
28420 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
28421 <table id="searchObjResults">
28422 <tr><th>Name</th><th>Phone</th></tr>
28423 <tr ng-repeat="friendObj in friends | filter:search:strict">
28424 <td>{{friendObj.name}}</td>
28425 <td>{{friendObj.phone}}</td>
28429 <file name="protractor.js" type="protractor">
28430 var expectFriendNames = function(expectedNames, key) {
28431 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
28432 arr.forEach(function(wd, i) {
28433 expect(wd.getText()).toMatch(expectedNames[i]);
28438 it('should search across all fields when filtering with a string', function() {
28439 var searchText = element(by.model('searchText'));
28440 searchText.clear();
28441 searchText.sendKeys('m');
28442 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
28444 searchText.clear();
28445 searchText.sendKeys('76');
28446 expectFriendNames(['John', 'Julie'], 'friend');
28449 it('should search in specific fields when filtering with a predicate object', function() {
28450 var searchAny = element(by.model('search.$'));
28452 searchAny.sendKeys('i');
28453 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
28455 it('should use a equal comparison when comparator is true', function() {
28456 var searchName = element(by.model('search.name'));
28457 var strict = element(by.model('strict'));
28458 searchName.clear();
28459 searchName.sendKeys('Julie');
28461 expectFriendNames(['Julie'], 'friendObj');
28466 function filterFilter() {
28467 return function(array, expression, comparator) {
28468 if (!isArrayLike(array)) {
28469 if (array == null) {
28472 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
28476 var expressionType = getTypeForFilter(expression);
28478 var matchAgainstAnyProp;
28480 switch (expressionType) {
28482 predicateFn = expression;
28488 matchAgainstAnyProp = true;
28492 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
28498 return Array.prototype.filter.call(array, predicateFn);
28502 // Helper functions for `filterFilter`
28503 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
28504 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
28507 if (comparator === true) {
28508 comparator = equals;
28509 } else if (!isFunction(comparator)) {
28510 comparator = function(actual, expected) {
28511 if (isUndefined(actual)) {
28512 // No substring matching against `undefined`
28515 if ((actual === null) || (expected === null)) {
28516 // No substring matching against `null`; only match against `null`
28517 return actual === expected;
28519 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
28520 // Should not compare primitives against objects, unless they have custom `toString` method
28524 actual = lowercase('' + actual);
28525 expected = lowercase('' + expected);
28526 return actual.indexOf(expected) !== -1;
28530 predicateFn = function(item) {
28531 if (shouldMatchPrimitives && !isObject(item)) {
28532 return deepCompare(item, expression.$, comparator, false);
28534 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
28537 return predicateFn;
28540 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
28541 var actualType = getTypeForFilter(actual);
28542 var expectedType = getTypeForFilter(expected);
28544 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
28545 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
28546 } else if (isArray(actual)) {
28547 // In case `actual` is an array, consider it a match
28548 // if ANY of it's items matches `expected`
28549 return actual.some(function(item) {
28550 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
28554 switch (actualType) {
28557 if (matchAgainstAnyProp) {
28558 for (key in actual) {
28559 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
28563 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
28564 } else if (expectedType === 'object') {
28565 for (key in expected) {
28566 var expectedVal = expected[key];
28567 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
28571 var matchAnyProperty = key === '$';
28572 var actualVal = matchAnyProperty ? actual : actual[key];
28573 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
28579 return comparator(actual, expected);
28585 return comparator(actual, expected);
28589 // Used for easily differentiating between `null` and actual `object`
28590 function getTypeForFilter(val) {
28591 return (val === null) ? 'null' : typeof val;
28594 var MAX_DIGITS = 22;
28595 var DECIMAL_SEP = '.';
28596 var ZERO_CHAR = '0';
28604 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
28605 * symbol for current locale is used.
28607 * @param {number} amount Input to filter.
28608 * @param {string=} symbol Currency symbol or identifier to be displayed.
28609 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
28610 * @returns {string} Formatted number.
28614 <example module="currencyExample">
28615 <file name="index.html">
28617 angular.module('currencyExample', [])
28618 .controller('ExampleController', ['$scope', function($scope) {
28619 $scope.amount = 1234.56;
28622 <div ng-controller="ExampleController">
28623 <input type="number" ng-model="amount" aria-label="amount"> <br>
28624 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
28625 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
28626 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
28629 <file name="protractor.js" type="protractor">
28630 it('should init with 1234.56', function() {
28631 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
28632 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
28633 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
28635 it('should update', function() {
28636 if (browser.params.browser == 'safari') {
28637 // Safari does not understand the minus key. See
28638 // https://github.com/angular/protractor/issues/481
28641 element(by.model('amount')).clear();
28642 element(by.model('amount')).sendKeys('-1234');
28643 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
28644 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
28645 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
28650 currencyFilter.$inject = ['$locale'];
28651 function currencyFilter($locale) {
28652 var formats = $locale.NUMBER_FORMATS;
28653 return function(amount, currencySymbol, fractionSize) {
28654 if (isUndefined(currencySymbol)) {
28655 currencySymbol = formats.CURRENCY_SYM;
28658 if (isUndefined(fractionSize)) {
28659 fractionSize = formats.PATTERNS[1].maxFrac;
28662 // if null or undefined pass it through
28663 return (amount == null)
28665 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
28666 replace(/\u00A4/g, currencySymbol);
28676 * Formats a number as text.
28678 * If the input is null or undefined, it will just be returned.
28679 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
28680 * If the input is not a number an empty string is returned.
28683 * @param {number|string} number Number to format.
28684 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
28685 * If this is not provided then the fraction size is computed from the current locale's number
28686 * formatting pattern. In the case of the default locale, it will be 3.
28687 * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
28690 <example module="numberFilterExample">
28691 <file name="index.html">
28693 angular.module('numberFilterExample', [])
28694 .controller('ExampleController', ['$scope', function($scope) {
28695 $scope.val = 1234.56789;
28698 <div ng-controller="ExampleController">
28699 <label>Enter number: <input ng-model='val'></label><br>
28700 Default formatting: <span id='number-default'>{{val | number}}</span><br>
28701 No fractions: <span>{{val | number:0}}</span><br>
28702 Negative number: <span>{{-val | number:4}}</span>
28705 <file name="protractor.js" type="protractor">
28706 it('should format numbers', function() {
28707 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
28708 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
28709 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
28712 it('should update', function() {
28713 element(by.model('val')).clear();
28714 element(by.model('val')).sendKeys('3374.333');
28715 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
28716 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
28717 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
28722 numberFilter.$inject = ['$locale'];
28723 function numberFilter($locale) {
28724 var formats = $locale.NUMBER_FORMATS;
28725 return function(number, fractionSize) {
28727 // if null or undefined pass it through
28728 return (number == null)
28730 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
28736 * Parse a number (as a string) into three components that can be used
28737 * for formatting the number.
28739 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
28741 * @param {string} numStr The number to parse
28742 * @return {object} An object describing this number, containing the following keys:
28743 * - d : an array of digits containing leading zeros as necessary
28744 * - i : the number of the digits in `d` that are to the left of the decimal point
28745 * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
28748 function parse(numStr) {
28749 var exponent = 0, digits, numberOfIntegerDigits;
28753 if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
28754 numStr = numStr.replace(DECIMAL_SEP, '');
28757 // Exponential form?
28758 if ((i = numStr.search(/e/i)) > 0) {
28759 // Work out the exponent.
28760 if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
28761 numberOfIntegerDigits += +numStr.slice(i + 1);
28762 numStr = numStr.substring(0, i);
28763 } else if (numberOfIntegerDigits < 0) {
28764 // There was no decimal point or exponent so it is an integer.
28765 numberOfIntegerDigits = numStr.length;
28768 // Count the number of leading zeros.
28769 for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++);
28771 if (i == (zeros = numStr.length)) {
28772 // The digits are all zero.
28774 numberOfIntegerDigits = 1;
28776 // Count the number of trailing zeros
28778 while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
28780 // Trailing zeros are insignificant so ignore them
28781 numberOfIntegerDigits -= i;
28783 // Convert string to array of digits without leading/trailing zeros.
28784 for (j = 0; i <= zeros; i++, j++) {
28785 digits[j] = +numStr.charAt(i);
28789 // If the number overflows the maximum allowed digits then use an exponent.
28790 if (numberOfIntegerDigits > MAX_DIGITS) {
28791 digits = digits.splice(0, MAX_DIGITS - 1);
28792 exponent = numberOfIntegerDigits - 1;
28793 numberOfIntegerDigits = 1;
28796 return { d: digits, e: exponent, i: numberOfIntegerDigits };
28800 * Round the parsed number to the specified number of decimal places
28801 * This function changed the parsedNumber in-place
28803 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
28804 var digits = parsedNumber.d;
28805 var fractionLen = digits.length - parsedNumber.i;
28807 // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
28808 fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
28810 // The index of the digit to where rounding is to occur
28811 var roundAt = fractionSize + parsedNumber.i;
28812 var digit = digits[roundAt];
28815 digits.splice(roundAt);
28817 // We rounded to zero so reset the parsedNumber
28818 parsedNumber.i = 1;
28819 digits.length = roundAt = fractionSize + 1;
28820 for (var i=0; i < roundAt; i++) digits[i] = 0;
28823 if (digit >= 5) digits[roundAt - 1]++;
28825 // Pad out with zeros to get the required fraction length
28826 for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
28829 // Do any carrying, e.g. a digit was rounded up to 10
28830 var carry = digits.reduceRight(function(carry, d, i, digits) {
28832 digits[i] = d % 10;
28833 return Math.floor(d / 10);
28836 digits.unshift(carry);
28842 * Format a number into a string
28843 * @param {number} number The number to format
28845 * minFrac, // the minimum number of digits required in the fraction part of the number
28846 * maxFrac, // the maximum number of digits required in the fraction part of the number
28847 * gSize, // number of digits in each group of separated digits
28848 * lgSize, // number of digits in the last group of digits before the decimal separator
28849 * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
28850 * posPre, // the string to go in front of a positive number
28851 * negSuf, // the string to go after a negative number (e.g. `)`)
28852 * posSuf // the string to go after a positive number
28854 * @param {string} groupSep The string to separate groups of number (e.g. `,`)
28855 * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
28856 * @param {[type]} fractionSize The size of the fractional part of the number
28857 * @return {string} The number formatted as a string
28859 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
28861 if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
28863 var isInfinity = !isFinite(number);
28864 var isZero = false;
28865 var numStr = Math.abs(number) + '',
28866 formattedText = '',
28870 formattedText = '\u221e';
28872 parsedNumber = parse(numStr);
28874 roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
28876 var digits = parsedNumber.d;
28877 var integerLen = parsedNumber.i;
28878 var exponent = parsedNumber.e;
28880 isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
28882 // pad zeros for small numbers
28883 while (integerLen < 0) {
28888 // extract decimals digits
28889 if (integerLen > 0) {
28890 decimals = digits.splice(integerLen);
28896 // format the integer digits with grouping separators
28898 if (digits.length > pattern.lgSize) {
28899 groups.unshift(digits.splice(-pattern.lgSize).join(''));
28901 while (digits.length > pattern.gSize) {
28902 groups.unshift(digits.splice(-pattern.gSize).join(''));
28904 if (digits.length) {
28905 groups.unshift(digits.join(''));
28907 formattedText = groups.join(groupSep);
28909 // append the decimal digits
28910 if (decimals.length) {
28911 formattedText += decimalSep + decimals.join('');
28915 formattedText += 'e+' + exponent;
28918 if (number < 0 && !isZero) {
28919 return pattern.negPre + formattedText + pattern.negSuf;
28921 return pattern.posPre + formattedText + pattern.posSuf;
28925 function padNumber(num, digits, trim) {
28932 while (num.length < digits) num = ZERO_CHAR + num;
28934 num = num.substr(num.length - digits);
28940 function dateGetter(name, size, offset, trim) {
28941 offset = offset || 0;
28942 return function(date) {
28943 var value = date['get' + name]();
28944 if (offset > 0 || value > -offset) {
28947 if (value === 0 && offset == -12) value = 12;
28948 return padNumber(value, size, trim);
28952 function dateStrGetter(name, shortForm) {
28953 return function(date, formats) {
28954 var value = date['get' + name]();
28955 var get = uppercase(shortForm ? ('SHORT' + name) : name);
28957 return formats[get][value];
28961 function timeZoneGetter(date, formats, offset) {
28962 var zone = -1 * offset;
28963 var paddedZone = (zone >= 0) ? "+" : "";
28965 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
28966 padNumber(Math.abs(zone % 60), 2);
28971 function getFirstThursdayOfYear(year) {
28972 // 0 = index of January
28973 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
28974 // 4 = index of Thursday (+1 to account for 1st = 5)
28975 // 11 = index of *next* Thursday (+1 account for 1st = 12)
28976 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
28979 function getThursdayThisWeek(datetime) {
28980 return new Date(datetime.getFullYear(), datetime.getMonth(),
28981 // 4 = index of Thursday
28982 datetime.getDate() + (4 - datetime.getDay()));
28985 function weekGetter(size) {
28986 return function(date) {
28987 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
28988 thisThurs = getThursdayThisWeek(date);
28990 var diff = +thisThurs - +firstThurs,
28991 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
28993 return padNumber(result, size);
28997 function ampmGetter(date, formats) {
28998 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
29001 function eraGetter(date, formats) {
29002 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
29005 function longEraGetter(date, formats) {
29006 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
29009 var DATE_FORMATS = {
29010 yyyy: dateGetter('FullYear', 4),
29011 yy: dateGetter('FullYear', 2, 0, true),
29012 y: dateGetter('FullYear', 1),
29013 MMMM: dateStrGetter('Month'),
29014 MMM: dateStrGetter('Month', true),
29015 MM: dateGetter('Month', 2, 1),
29016 M: dateGetter('Month', 1, 1),
29017 dd: dateGetter('Date', 2),
29018 d: dateGetter('Date', 1),
29019 HH: dateGetter('Hours', 2),
29020 H: dateGetter('Hours', 1),
29021 hh: dateGetter('Hours', 2, -12),
29022 h: dateGetter('Hours', 1, -12),
29023 mm: dateGetter('Minutes', 2),
29024 m: dateGetter('Minutes', 1),
29025 ss: dateGetter('Seconds', 2),
29026 s: dateGetter('Seconds', 1),
29027 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
29028 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
29029 sss: dateGetter('Milliseconds', 3),
29030 EEEE: dateStrGetter('Day'),
29031 EEE: dateStrGetter('Day', true),
29039 GGGG: longEraGetter
29042 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
29043 NUMBER_STRING = /^\-?\d+$/;
29051 * Formats `date` to a string based on the requested `format`.
29053 * `format` string can be composed of the following elements:
29055 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
29056 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
29057 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
29058 * * `'MMMM'`: Month in year (January-December)
29059 * * `'MMM'`: Month in year (Jan-Dec)
29060 * * `'MM'`: Month in year, padded (01-12)
29061 * * `'M'`: Month in year (1-12)
29062 * * `'dd'`: Day in month, padded (01-31)
29063 * * `'d'`: Day in month (1-31)
29064 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
29065 * * `'EEE'`: Day in Week, (Sun-Sat)
29066 * * `'HH'`: Hour in day, padded (00-23)
29067 * * `'H'`: Hour in day (0-23)
29068 * * `'hh'`: Hour in AM/PM, padded (01-12)
29069 * * `'h'`: Hour in AM/PM, (1-12)
29070 * * `'mm'`: Minute in hour, padded (00-59)
29071 * * `'m'`: Minute in hour (0-59)
29072 * * `'ss'`: Second in minute, padded (00-59)
29073 * * `'s'`: Second in minute (0-59)
29074 * * `'sss'`: Millisecond in second, padded (000-999)
29075 * * `'a'`: AM/PM marker
29076 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
29077 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
29078 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
29079 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
29080 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
29082 * `format` string can also be one of the following predefined
29083 * {@link guide/i18n localizable formats}:
29085 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
29086 * (e.g. Sep 3, 2010 12:05:08 PM)
29087 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
29088 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
29089 * (e.g. Friday, September 3, 2010)
29090 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
29091 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
29092 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
29093 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
29094 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
29096 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
29097 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
29098 * (e.g. `"h 'o''clock'"`).
29100 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
29101 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
29102 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
29103 * specified in the string input, the time is considered to be in the local timezone.
29104 * @param {string=} format Formatting rules (see Description). If not specified,
29105 * `mediumDate` is used.
29106 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
29107 * continental US time zone abbreviations, but for general use, use a time zone offset, for
29108 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
29109 * If not specified, the timezone of the browser will be used.
29110 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
29114 <file name="index.html">
29115 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
29116 <span>{{1288323623006 | date:'medium'}}</span><br>
29117 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
29118 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
29119 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
29120 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
29121 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
29122 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
29124 <file name="protractor.js" type="protractor">
29125 it('should format date', function() {
29126 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
29127 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
29128 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
29129 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
29130 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
29131 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
29132 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
29133 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
29138 dateFilter.$inject = ['$locale'];
29139 function dateFilter($locale) {
29142 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
29143 // 1 2 3 4 5 6 7 8 9 10 11
29144 function jsonStringToDate(string) {
29146 if (match = string.match(R_ISO8601_STR)) {
29147 var date = new Date(0),
29150 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
29151 timeSetter = match[8] ? date.setUTCHours : date.setHours;
29154 tzHour = toInt(match[9] + match[10]);
29155 tzMin = toInt(match[9] + match[11]);
29157 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
29158 var h = toInt(match[4] || 0) - tzHour;
29159 var m = toInt(match[5] || 0) - tzMin;
29160 var s = toInt(match[6] || 0);
29161 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
29162 timeSetter.call(date, h, m, s, ms);
29169 return function(date, format, timezone) {
29174 format = format || 'mediumDate';
29175 format = $locale.DATETIME_FORMATS[format] || format;
29176 if (isString(date)) {
29177 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
29180 if (isNumber(date)) {
29181 date = new Date(date);
29184 if (!isDate(date) || !isFinite(date.getTime())) {
29189 match = DATE_FORMATS_SPLIT.exec(format);
29191 parts = concat(parts, match, 1);
29192 format = parts.pop();
29194 parts.push(format);
29199 var dateTimezoneOffset = date.getTimezoneOffset();
29201 dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
29202 date = convertTimezoneToLocal(date, timezone, true);
29204 forEach(parts, function(value) {
29205 fn = DATE_FORMATS[value];
29206 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
29207 : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
29221 * Allows you to convert a JavaScript object into JSON string.
29223 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
29224 * the binding is automatically converted to JSON.
29226 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
29227 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
29228 * @returns {string} JSON string.
29233 <file name="index.html">
29234 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
29235 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
29237 <file name="protractor.js" type="protractor">
29238 it('should jsonify filtered objects', function() {
29239 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
29240 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
29246 function jsonFilter() {
29247 return function(object, spacing) {
29248 if (isUndefined(spacing)) {
29251 return toJson(object, spacing);
29261 * Converts string to lowercase.
29262 * @see angular.lowercase
29264 var lowercaseFilter = valueFn(lowercase);
29272 * Converts string to uppercase.
29273 * @see angular.uppercase
29275 var uppercaseFilter = valueFn(uppercase);
29283 * Creates a new array or string containing only a specified number of elements. The elements
29284 * are taken from either the beginning or the end of the source array, string or number, as specified by
29285 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
29286 * converted to a string.
29288 * @param {Array|string|number} input Source array, string or number to be limited.
29289 * @param {string|number} limit The length of the returned array or string. If the `limit` number
29290 * is positive, `limit` number of items from the beginning of the source array/string are copied.
29291 * If the number is negative, `limit` number of items from the end of the source array/string
29292 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
29293 * the input will be returned unchanged.
29294 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
29295 * indicates an offset from the end of `input`. Defaults to `0`.
29296 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
29297 * had less than `limit` elements.
29300 <example module="limitToExample">
29301 <file name="index.html">
29303 angular.module('limitToExample', [])
29304 .controller('ExampleController', ['$scope', function($scope) {
29305 $scope.numbers = [1,2,3,4,5,6,7,8,9];
29306 $scope.letters = "abcdefghi";
29307 $scope.longNumber = 2345432342;
29308 $scope.numLimit = 3;
29309 $scope.letterLimit = 3;
29310 $scope.longNumberLimit = 3;
29313 <div ng-controller="ExampleController">
29315 Limit {{numbers}} to:
29316 <input type="number" step="1" ng-model="numLimit">
29318 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
29320 Limit {{letters}} to:
29321 <input type="number" step="1" ng-model="letterLimit">
29323 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
29325 Limit {{longNumber}} to:
29326 <input type="number" step="1" ng-model="longNumberLimit">
29328 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
29331 <file name="protractor.js" type="protractor">
29332 var numLimitInput = element(by.model('numLimit'));
29333 var letterLimitInput = element(by.model('letterLimit'));
29334 var longNumberLimitInput = element(by.model('longNumberLimit'));
29335 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
29336 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
29337 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
29339 it('should limit the number array to first three items', function() {
29340 expect(numLimitInput.getAttribute('value')).toBe('3');
29341 expect(letterLimitInput.getAttribute('value')).toBe('3');
29342 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
29343 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
29344 expect(limitedLetters.getText()).toEqual('Output letters: abc');
29345 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
29348 // There is a bug in safari and protractor that doesn't like the minus key
29349 // it('should update the output when -3 is entered', function() {
29350 // numLimitInput.clear();
29351 // numLimitInput.sendKeys('-3');
29352 // letterLimitInput.clear();
29353 // letterLimitInput.sendKeys('-3');
29354 // longNumberLimitInput.clear();
29355 // longNumberLimitInput.sendKeys('-3');
29356 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
29357 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
29358 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
29361 it('should not exceed the maximum size of input array', function() {
29362 numLimitInput.clear();
29363 numLimitInput.sendKeys('100');
29364 letterLimitInput.clear();
29365 letterLimitInput.sendKeys('100');
29366 longNumberLimitInput.clear();
29367 longNumberLimitInput.sendKeys('100');
29368 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
29369 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
29370 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
29375 function limitToFilter() {
29376 return function(input, limit, begin) {
29377 if (Math.abs(Number(limit)) === Infinity) {
29378 limit = Number(limit);
29380 limit = toInt(limit);
29382 if (isNaN(limit)) return input;
29384 if (isNumber(input)) input = input.toString();
29385 if (!isArray(input) && !isString(input)) return input;
29387 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
29388 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
29391 return input.slice(begin, begin + limit);
29394 return input.slice(limit, input.length);
29396 return input.slice(Math.max(0, begin + limit), begin);
29408 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
29409 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
29410 * as expected, make sure they are actually being saved as numbers and not strings.
29411 * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
29413 * @param {Array} array The array (or array-like object) to sort.
29414 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
29415 * used by the comparator to determine the order of elements.
29419 * - `function`: Getter function. The result of this function will be sorted using the
29420 * `<`, `===`, `>` operator.
29421 * - `string`: An Angular expression. The result of this expression is used to compare elements
29422 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
29423 * 3 first characters of a property called `name`). The result of a constant expression
29424 * is interpreted as a property name to be used in comparisons (for example `"special name"`
29425 * to sort object by the value of their `special name` property). An expression can be
29426 * optionally prefixed with `+` or `-` to control ascending or descending sort order
29427 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
29428 * element itself is used to compare where sorting.
29429 * - `Array`: An array of function or string predicates. The first predicate in the array
29430 * is used for sorting, but when two items are equivalent, the next predicate is used.
29432 * If the predicate is missing or empty then it defaults to `'+'`.
29434 * @param {boolean=} reverse Reverse the order of the array.
29435 * @returns {Array} Sorted copy of the source array.
29439 * The example below demonstrates a simple ngRepeat, where the data is sorted
29440 * by age in descending order (predicate is set to `'-age'`).
29441 * `reverse` is not set, which means it defaults to `false`.
29442 <example module="orderByExample">
29443 <file name="index.html">
29444 <div ng-controller="ExampleController">
29445 <table class="friend">
29448 <th>Phone Number</th>
29451 <tr ng-repeat="friend in friends | orderBy:'-age'">
29452 <td>{{friend.name}}</td>
29453 <td>{{friend.phone}}</td>
29454 <td>{{friend.age}}</td>
29459 <file name="script.js">
29460 angular.module('orderByExample', [])
29461 .controller('ExampleController', ['$scope', function($scope) {
29463 [{name:'John', phone:'555-1212', age:10},
29464 {name:'Mary', phone:'555-9876', age:19},
29465 {name:'Mike', phone:'555-4321', age:21},
29466 {name:'Adam', phone:'555-5678', age:35},
29467 {name:'Julie', phone:'555-8765', age:29}];
29472 * The predicate and reverse parameters can be controlled dynamically through scope properties,
29473 * as shown in the next example.
29475 <example module="orderByExample">
29476 <file name="index.html">
29477 <div ng-controller="ExampleController">
29478 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
29480 <button ng-click="predicate=''">Set to unsorted</button>
29481 <table class="friend">
29484 <button ng-click="order('name')">Name</button>
29485 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
29488 <button ng-click="order('phone')">Phone Number</button>
29489 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
29492 <button ng-click="order('age')">Age</button>
29493 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
29496 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
29497 <td>{{friend.name}}</td>
29498 <td>{{friend.phone}}</td>
29499 <td>{{friend.age}}</td>
29504 <file name="script.js">
29505 angular.module('orderByExample', [])
29506 .controller('ExampleController', ['$scope', function($scope) {
29508 [{name:'John', phone:'555-1212', age:10},
29509 {name:'Mary', phone:'555-9876', age:19},
29510 {name:'Mike', phone:'555-4321', age:21},
29511 {name:'Adam', phone:'555-5678', age:35},
29512 {name:'Julie', phone:'555-8765', age:29}];
29513 $scope.predicate = 'age';
29514 $scope.reverse = true;
29515 $scope.order = function(predicate) {
29516 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
29517 $scope.predicate = predicate;
29521 <file name="style.css">
29525 .sortorder.reverse:after {
29531 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
29532 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
29533 * desired parameters.
29538 <example module="orderByExample">
29539 <file name="index.html">
29540 <div ng-controller="ExampleController">
29541 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
29542 <table class="friend">
29545 <button ng-click="order('name')">Name</button>
29546 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
29549 <button ng-click="order('phone')">Phone Number</button>
29550 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
29553 <button ng-click="order('age')">Age</button>
29554 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
29557 <tr ng-repeat="friend in friends">
29558 <td>{{friend.name}}</td>
29559 <td>{{friend.phone}}</td>
29560 <td>{{friend.age}}</td>
29566 <file name="script.js">
29567 angular.module('orderByExample', [])
29568 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
29569 var orderBy = $filter('orderBy');
29571 { name: 'John', phone: '555-1212', age: 10 },
29572 { name: 'Mary', phone: '555-9876', age: 19 },
29573 { name: 'Mike', phone: '555-4321', age: 21 },
29574 { name: 'Adam', phone: '555-5678', age: 35 },
29575 { name: 'Julie', phone: '555-8765', age: 29 }
29577 $scope.order = function(predicate) {
29578 $scope.predicate = predicate;
29579 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
29580 $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
29582 $scope.order('age', true);
29586 <file name="style.css">
29590 .sortorder.reverse:after {
29596 orderByFilter.$inject = ['$parse'];
29597 function orderByFilter($parse) {
29598 return function(array, sortPredicate, reverseOrder) {
29600 if (array == null) return array;
29601 if (!isArrayLike(array)) {
29602 throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
29605 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
29606 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
29608 var predicates = processPredicates(sortPredicate, reverseOrder);
29609 // Add a predicate at the end that evaluates to the element index. This makes the
29610 // sort stable as it works as a tie-breaker when all the input predicates cannot
29611 // distinguish between two elements.
29612 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
29614 // The next three lines are a version of a Swartzian Transform idiom from Perl
29615 // (sometimes called the Decorate-Sort-Undecorate idiom)
29616 // See https://en.wikipedia.org/wiki/Schwartzian_transform
29617 var compareValues = Array.prototype.map.call(array, getComparisonObject);
29618 compareValues.sort(doComparison);
29619 array = compareValues.map(function(item) { return item.value; });
29623 function getComparisonObject(value, index) {
29626 predicateValues: predicates.map(function(predicate) {
29627 return getPredicateValue(predicate.get(value), index);
29632 function doComparison(v1, v2) {
29634 for (var index=0, length = predicates.length; index < length; ++index) {
29635 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
29642 function processPredicates(sortPredicate, reverseOrder) {
29643 reverseOrder = reverseOrder ? -1 : 1;
29644 return sortPredicate.map(function(predicate) {
29645 var descending = 1, get = identity;
29647 if (isFunction(predicate)) {
29649 } else if (isString(predicate)) {
29650 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
29651 descending = predicate.charAt(0) == '-' ? -1 : 1;
29652 predicate = predicate.substring(1);
29654 if (predicate !== '') {
29655 get = $parse(predicate);
29656 if (get.constant) {
29658 get = function(value) { return value[key]; };
29662 return { get: get, descending: descending * reverseOrder };
29666 function isPrimitive(value) {
29667 switch (typeof value) {
29668 case 'number': /* falls through */
29669 case 'boolean': /* falls through */
29677 function objectValue(value, index) {
29678 // If `valueOf` is a valid function use that
29679 if (typeof value.valueOf === 'function') {
29680 value = value.valueOf();
29681 if (isPrimitive(value)) return value;
29683 // If `toString` is a valid function and not the one from `Object.prototype` use that
29684 if (hasCustomToString(value)) {
29685 value = value.toString();
29686 if (isPrimitive(value)) return value;
29688 // We have a basic object so we use the position of the object in the collection
29692 function getPredicateValue(value, index) {
29693 var type = typeof value;
29694 if (value === null) {
29697 } else if (type === 'string') {
29698 value = value.toLowerCase();
29699 } else if (type === 'object') {
29700 value = objectValue(value, index);
29702 return { value: value, type: type };
29705 function compare(v1, v2) {
29707 if (v1.type === v2.type) {
29708 if (v1.value !== v2.value) {
29709 result = v1.value < v2.value ? -1 : 1;
29712 result = v1.type < v2.type ? -1 : 1;
29718 function ngDirective(directive) {
29719 if (isFunction(directive)) {
29724 directive.restrict = directive.restrict || 'AC';
29725 return valueFn(directive);
29734 * Modifies the default behavior of the html A tag so that the default action is prevented when
29735 * the href attribute is empty.
29737 * This change permits the easy creation of action links with the `ngClick` directive
29738 * without changing the location or causing page reloads, e.g.:
29739 * `<a href="" ng-click="list.addItem()">Add Item</a>`
29741 var htmlAnchorDirective = valueFn({
29743 compile: function(element, attr) {
29744 if (!attr.href && !attr.xlinkHref) {
29745 return function(scope, element) {
29746 // If the linked element is not an anchor tag anymore, do nothing
29747 if (element[0].nodeName.toLowerCase() !== 'a') return;
29749 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
29750 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
29751 'xlink:href' : 'href';
29752 element.on('click', function(event) {
29753 // if we have no href url, then don't navigate anywhere.
29754 if (!element.attr(href)) {
29755 event.preventDefault();
29770 * Using Angular markup like `{{hash}}` in an href attribute will
29771 * make the link go to the wrong URL if the user clicks it before
29772 * Angular has a chance to replace the `{{hash}}` markup with its
29773 * value. Until Angular replaces the markup the link will be broken
29774 * and will most likely return a 404 error. The `ngHref` directive
29775 * solves this problem.
29777 * The wrong way to write it:
29779 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
29782 * The correct way to write it:
29784 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
29788 * @param {template} ngHref any string which can contain `{{}}` markup.
29791 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
29792 * in links and their different behaviors:
29794 <file name="index.html">
29795 <input ng-model="value" /><br />
29796 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
29797 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
29798 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
29799 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
29800 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
29801 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
29803 <file name="protractor.js" type="protractor">
29804 it('should execute ng-click but not reload when href without value', function() {
29805 element(by.id('link-1')).click();
29806 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
29807 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
29810 it('should execute ng-click but not reload when href empty string', function() {
29811 element(by.id('link-2')).click();
29812 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
29813 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
29816 it('should execute ng-click and change url when ng-href specified', function() {
29817 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
29819 element(by.id('link-3')).click();
29821 // At this point, we navigate away from an Angular page, so we need
29822 // to use browser.driver to get the base webdriver.
29824 browser.wait(function() {
29825 return browser.driver.getCurrentUrl().then(function(url) {
29826 return url.match(/\/123$/);
29828 }, 5000, 'page should navigate to /123');
29831 it('should execute ng-click but not reload when href empty string and name specified', function() {
29832 element(by.id('link-4')).click();
29833 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
29834 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
29837 it('should execute ng-click but not reload when no href but name specified', function() {
29838 element(by.id('link-5')).click();
29839 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
29840 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
29843 it('should only change url when only ng-href', function() {
29844 element(by.model('value')).clear();
29845 element(by.model('value')).sendKeys('6');
29846 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
29848 element(by.id('link-6')).click();
29850 // At this point, we navigate away from an Angular page, so we need
29851 // to use browser.driver to get the base webdriver.
29852 browser.wait(function() {
29853 return browser.driver.getCurrentUrl().then(function(url) {
29854 return url.match(/\/6$/);
29856 }, 5000, 'page should navigate to /6');
29869 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
29870 * work right: The browser will fetch from the URL with the literal
29871 * text `{{hash}}` until Angular replaces the expression inside
29872 * `{{hash}}`. The `ngSrc` directive solves this problem.
29874 * The buggy way to write it:
29876 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
29879 * The correct way to write it:
29881 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
29885 * @param {template} ngSrc any string which can contain `{{}}` markup.
29895 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
29896 * work right: The browser will fetch from the URL with the literal
29897 * text `{{hash}}` until Angular replaces the expression inside
29898 * `{{hash}}`. The `ngSrcset` directive solves this problem.
29900 * The buggy way to write it:
29902 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
29905 * The correct way to write it:
29907 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
29911 * @param {template} ngSrcset any string which can contain `{{}}` markup.
29922 * This directive sets the `disabled` attribute on the element if the
29923 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
29925 * A special directive is necessary because we cannot use interpolation inside the `disabled`
29926 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
29930 <file name="index.html">
29931 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
29932 <button ng-model="button" ng-disabled="checked">Button</button>
29934 <file name="protractor.js" type="protractor">
29935 it('should toggle button', function() {
29936 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
29937 element(by.model('checked')).click();
29938 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
29944 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
29945 * then the `disabled` attribute will be set on the element
29956 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
29958 * Note that this directive should not be used together with {@link ngModel `ngModel`},
29959 * as this can lead to unexpected behavior.
29961 * A special directive is necessary because we cannot use interpolation inside the `checked`
29962 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
29966 <file name="index.html">
29967 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
29968 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
29970 <file name="protractor.js" type="protractor">
29971 it('should check both checkBoxes', function() {
29972 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
29973 element(by.model('master')).click();
29974 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
29980 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
29981 * then the `checked` attribute will be set on the element
29993 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
29995 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
29996 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30000 <file name="index.html">
30001 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
30002 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
30004 <file name="protractor.js" type="protractor">
30005 it('should toggle readonly attr', function() {
30006 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
30007 element(by.model('checked')).click();
30008 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
30014 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
30015 * then special attribute "readonly" will be set on the element
30027 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
30029 * A special directive is necessary because we cannot use interpolation inside the `selected`
30030 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30034 <file name="index.html">
30035 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
30036 <select aria-label="ngSelected demo">
30037 <option>Hello!</option>
30038 <option id="greet" ng-selected="selected">Greetings!</option>
30041 <file name="protractor.js" type="protractor">
30042 it('should select Greetings!', function() {
30043 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
30044 element(by.model('selected')).click();
30045 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
30051 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
30052 * then special attribute "selected" will be set on the element
30063 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
30065 * A special directive is necessary because we cannot use interpolation inside the `open`
30066 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
30070 <file name="index.html">
30071 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
30072 <details id="details" ng-open="open">
30073 <summary>Show/Hide me</summary>
30076 <file name="protractor.js" type="protractor">
30077 it('should toggle open', function() {
30078 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
30079 element(by.model('open')).click();
30080 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
30086 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
30087 * then special attribute "open" will be set on the element
30090 var ngAttributeAliasDirectives = {};
30092 // boolean attrs are evaluated
30093 forEach(BOOLEAN_ATTR, function(propName, attrName) {
30094 // binding to multiple is not supported
30095 if (propName == "multiple") return;
30097 function defaultLinkFn(scope, element, attr) {
30098 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
30099 attr.$set(attrName, !!value);
30103 var normalized = directiveNormalize('ng-' + attrName);
30104 var linkFn = defaultLinkFn;
30106 if (propName === 'checked') {
30107 linkFn = function(scope, element, attr) {
30108 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
30109 if (attr.ngModel !== attr[normalized]) {
30110 defaultLinkFn(scope, element, attr);
30115 ngAttributeAliasDirectives[normalized] = function() {
30124 // aliased input attrs are evaluated
30125 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
30126 ngAttributeAliasDirectives[ngAttr] = function() {
30129 link: function(scope, element, attr) {
30130 //special case ngPattern when a literal regular expression value
30131 //is used as the expression (this way we don't have to watch anything).
30132 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
30133 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
30135 attr.$set("ngPattern", new RegExp(match[1], match[2]));
30140 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
30141 attr.$set(ngAttr, value);
30148 // ng-src, ng-srcset, ng-href are interpolated
30149 forEach(['src', 'srcset', 'href'], function(attrName) {
30150 var normalized = directiveNormalize('ng-' + attrName);
30151 ngAttributeAliasDirectives[normalized] = function() {
30153 priority: 99, // it needs to run after the attributes are interpolated
30154 link: function(scope, element, attr) {
30155 var propName = attrName,
30158 if (attrName === 'href' &&
30159 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
30160 name = 'xlinkHref';
30161 attr.$attr[name] = 'xlink:href';
30165 attr.$observe(normalized, function(value) {
30167 if (attrName === 'href') {
30168 attr.$set(name, null);
30173 attr.$set(name, value);
30175 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
30176 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
30177 // to set the property as well to achieve the desired effect.
30178 // we use attr[attrName] value since $set can sanitize the url.
30179 if (msie && propName) element.prop(propName, attr[name]);
30186 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
30188 var nullFormCtrl = {
30190 $$renameControl: nullFormRenameControl,
30191 $removeControl: noop,
30192 $setValidity: noop,
30194 $setPristine: noop,
30195 $setSubmitted: noop
30197 SUBMITTED_CLASS = 'ng-submitted';
30199 function nullFormRenameControl(control, name) {
30200 control.$name = name;
30205 * @name form.FormController
30207 * @property {boolean} $pristine True if user has not interacted with the form yet.
30208 * @property {boolean} $dirty True if user has already interacted with the form.
30209 * @property {boolean} $valid True if all of the containing forms and controls are valid.
30210 * @property {boolean} $invalid True if at least one containing control or form is invalid.
30211 * @property {boolean} $pending True if at least one containing control or form is pending.
30212 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
30214 * @property {Object} $error Is an object hash, containing references to controls or
30215 * forms with failing validators, where:
30217 * - keys are validation tokens (error names),
30218 * - values are arrays of controls or forms that have a failing validator for given error name.
30220 * Built-in validation tokens:
30232 * - `datetimelocal`
30238 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
30239 * such as being valid/invalid or dirty/pristine.
30241 * Each {@link ng.directive:form form} directive creates an instance
30242 * of `FormController`.
30245 //asks for $scope to fool the BC controller module
30246 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
30247 function FormController(element, attrs, $scope, $animate, $interpolate) {
30253 form.$$success = {};
30254 form.$pending = undefined;
30255 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
30256 form.$dirty = false;
30257 form.$pristine = true;
30258 form.$valid = true;
30259 form.$invalid = false;
30260 form.$submitted = false;
30261 form.$$parentForm = nullFormCtrl;
30265 * @name form.FormController#$rollbackViewValue
30268 * Rollback all form controls pending updates to the `$modelValue`.
30270 * Updates may be pending by a debounced event or because the input is waiting for a some future
30271 * event defined in `ng-model-options`. This method is typically needed by the reset button of
30272 * a form that uses `ng-model-options` to pend updates.
30274 form.$rollbackViewValue = function() {
30275 forEach(controls, function(control) {
30276 control.$rollbackViewValue();
30282 * @name form.FormController#$commitViewValue
30285 * Commit all form controls pending updates to the `$modelValue`.
30287 * Updates may be pending by a debounced event or because the input is waiting for a some future
30288 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
30289 * usually handles calling this in response to input events.
30291 form.$commitViewValue = function() {
30292 forEach(controls, function(control) {
30293 control.$commitViewValue();
30299 * @name form.FormController#$addControl
30300 * @param {object} control control object, either a {@link form.FormController} or an
30301 * {@link ngModel.NgModelController}
30304 * Register a control with the form. Input elements using ngModelController do this automatically
30305 * when they are linked.
30307 * Note that the current state of the control will not be reflected on the new parent form. This
30308 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
30311 * However, if the method is used programmatically, for example by adding dynamically created controls,
30312 * or controls that have been previously removed without destroying their corresponding DOM element,
30313 * it's the developers responsibility to make sure the current state propagates to the parent form.
30315 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
30316 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
30318 form.$addControl = function(control) {
30319 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
30320 // and not added to the scope. Now we throw an error.
30321 assertNotHasOwnProperty(control.$name, 'input');
30322 controls.push(control);
30324 if (control.$name) {
30325 form[control.$name] = control;
30328 control.$$parentForm = form;
30331 // Private API: rename a form control
30332 form.$$renameControl = function(control, newName) {
30333 var oldName = control.$name;
30335 if (form[oldName] === control) {
30336 delete form[oldName];
30338 form[newName] = control;
30339 control.$name = newName;
30344 * @name form.FormController#$removeControl
30345 * @param {object} control control object, either a {@link form.FormController} or an
30346 * {@link ngModel.NgModelController}
30349 * Deregister a control from the form.
30351 * Input elements using ngModelController do this automatically when they are destroyed.
30353 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
30354 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
30355 * different from case to case. For example, removing the only `$dirty` control from a form may or
30356 * may not mean that the form is still `$dirty`.
30358 form.$removeControl = function(control) {
30359 if (control.$name && form[control.$name] === control) {
30360 delete form[control.$name];
30362 forEach(form.$pending, function(value, name) {
30363 form.$setValidity(name, null, control);
30365 forEach(form.$error, function(value, name) {
30366 form.$setValidity(name, null, control);
30368 forEach(form.$$success, function(value, name) {
30369 form.$setValidity(name, null, control);
30372 arrayRemove(controls, control);
30373 control.$$parentForm = nullFormCtrl;
30379 * @name form.FormController#$setValidity
30382 * Sets the validity of a form control.
30384 * This method will also propagate to parent forms.
30386 addSetValidityMethod({
30389 set: function(object, property, controller) {
30390 var list = object[property];
30392 object[property] = [controller];
30394 var index = list.indexOf(controller);
30395 if (index === -1) {
30396 list.push(controller);
30400 unset: function(object, property, controller) {
30401 var list = object[property];
30405 arrayRemove(list, controller);
30406 if (list.length === 0) {
30407 delete object[property];
30415 * @name form.FormController#$setDirty
30418 * Sets the form to a dirty state.
30420 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
30421 * state (ng-dirty class). This method will also propagate to parent forms.
30423 form.$setDirty = function() {
30424 $animate.removeClass(element, PRISTINE_CLASS);
30425 $animate.addClass(element, DIRTY_CLASS);
30426 form.$dirty = true;
30427 form.$pristine = false;
30428 form.$$parentForm.$setDirty();
30433 * @name form.FormController#$setPristine
30436 * Sets the form to its pristine state.
30438 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
30439 * state (ng-pristine class). This method will also propagate to all the controls contained
30442 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
30443 * saving or resetting it.
30445 form.$setPristine = function() {
30446 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
30447 form.$dirty = false;
30448 form.$pristine = true;
30449 form.$submitted = false;
30450 forEach(controls, function(control) {
30451 control.$setPristine();
30457 * @name form.FormController#$setUntouched
30460 * Sets the form to its untouched state.
30462 * This method can be called to remove the 'ng-touched' class and set the form controls to their
30463 * untouched state (ng-untouched class).
30465 * Setting a form controls back to their untouched state is often useful when setting the form
30466 * back to its pristine state.
30468 form.$setUntouched = function() {
30469 forEach(controls, function(control) {
30470 control.$setUntouched();
30476 * @name form.FormController#$setSubmitted
30479 * Sets the form to its submitted state.
30481 form.$setSubmitted = function() {
30482 $animate.addClass(element, SUBMITTED_CLASS);
30483 form.$submitted = true;
30484 form.$$parentForm.$setSubmitted();
30494 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
30495 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
30496 * sub-group of controls needs to be determined.
30498 * Note: the purpose of `ngForm` is to group controls,
30499 * but not to be a replacement for the `<form>` tag with all of its capabilities
30500 * (e.g. posting to the server, ...).
30502 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
30503 * related scope, under this name.
30513 * Directive that instantiates
30514 * {@link form.FormController FormController}.
30516 * If the `name` attribute is specified, the form controller is published onto the current scope under
30519 * # Alias: {@link ng.directive:ngForm `ngForm`}
30521 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
30522 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
30523 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
30524 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
30525 * of controls needs to be determined.
30528 * - `ng-valid` is set if the form is valid.
30529 * - `ng-invalid` is set if the form is invalid.
30530 * - `ng-pending` is set if the form is pending.
30531 * - `ng-pristine` is set if the form is pristine.
30532 * - `ng-dirty` is set if the form is dirty.
30533 * - `ng-submitted` is set if the form was submitted.
30535 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
30538 * # Submitting a form and preventing the default action
30540 * Since the role of forms in client-side Angular applications is different than in classical
30541 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
30542 * page reload that sends the data to the server. Instead some javascript logic should be triggered
30543 * to handle the form submission in an application-specific way.
30545 * For this reason, Angular prevents the default action (form submission to the server) unless the
30546 * `<form>` element has an `action` attribute specified.
30548 * You can use one of the following two ways to specify what javascript method should be called when
30549 * a form is submitted:
30551 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
30552 * - {@link ng.directive:ngClick ngClick} directive on the first
30553 * button or input field of type submit (input[type=submit])
30555 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
30556 * or {@link ng.directive:ngClick ngClick} directives.
30557 * This is because of the following form submission rules in the HTML specification:
30559 * - If a form has only one input field then hitting enter in this field triggers form submit
30561 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
30562 * doesn't trigger submit
30563 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
30564 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
30565 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
30567 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
30568 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
30569 * to have access to the updated model.
30571 * ## Animation Hooks
30573 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
30574 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
30575 * other validations that are performed within the form. Animations in ngForm are similar to how
30576 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
30577 * as JS animations.
30579 * The following example shows a simple way to utilize CSS transitions to style a form element
30580 * that has been rendered as invalid after it has been validated:
30583 * //be sure to include ngAnimate as a module to hook into more
30584 * //advanced animations
30586 * transition:0.5s linear all;
30587 * background: white;
30589 * .my-form.ng-invalid {
30596 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
30597 <file name="index.html">
30599 angular.module('formExample', [])
30600 .controller('FormController', ['$scope', function($scope) {
30601 $scope.userType = 'guest';
30606 transition:all linear 0.5s;
30607 background: transparent;
30609 .my-form.ng-invalid {
30613 <form name="myForm" ng-controller="FormController" class="my-form">
30614 userType: <input name="input" ng-model="userType" required>
30615 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
30616 <code>userType = {{userType}}</code><br>
30617 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
30618 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
30619 <code>myForm.$valid = {{myForm.$valid}}</code><br>
30620 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
30623 <file name="protractor.js" type="protractor">
30624 it('should initialize to model', function() {
30625 var userType = element(by.binding('userType'));
30626 var valid = element(by.binding('myForm.input.$valid'));
30628 expect(userType.getText()).toContain('guest');
30629 expect(valid.getText()).toContain('true');
30632 it('should be invalid if empty', function() {
30633 var userType = element(by.binding('userType'));
30634 var valid = element(by.binding('myForm.input.$valid'));
30635 var userInput = element(by.model('userType'));
30638 userInput.sendKeys('');
30640 expect(userType.getText()).toEqual('userType =');
30641 expect(valid.getText()).toContain('false');
30646 * @param {string=} name Name of the form. If specified, the form controller will be published into
30647 * related scope, under this name.
30649 var formDirectiveFactory = function(isNgForm) {
30650 return ['$timeout', '$parse', function($timeout, $parse) {
30651 var formDirective = {
30653 restrict: isNgForm ? 'EAC' : 'E',
30654 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
30655 controller: FormController,
30656 compile: function ngFormCompile(formElement, attr) {
30657 // Setup initial state of the control
30658 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
30660 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
30663 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
30664 var controller = ctrls[0];
30666 // if `action` attr is not present on the form, prevent the default action (submission)
30667 if (!('action' in attr)) {
30668 // we can't use jq events because if a form is destroyed during submission the default
30669 // action is not prevented. see #1238
30671 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
30672 // page reload if the form was destroyed by submission of the form via a click handler
30673 // on a button in the form. Looks like an IE9 specific bug.
30674 var handleFormSubmission = function(event) {
30675 scope.$apply(function() {
30676 controller.$commitViewValue();
30677 controller.$setSubmitted();
30680 event.preventDefault();
30683 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
30685 // unregister the preventDefault listener so that we don't not leak memory but in a
30686 // way that will achieve the prevention of the default action.
30687 formElement.on('$destroy', function() {
30688 $timeout(function() {
30689 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
30694 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
30695 parentFormCtrl.$addControl(controller);
30697 var setter = nameAttr ? getSetter(controller.$name) : noop;
30700 setter(scope, controller);
30701 attr.$observe(nameAttr, function(newValue) {
30702 if (controller.$name === newValue) return;
30703 setter(scope, undefined);
30704 controller.$$parentForm.$$renameControl(controller, newValue);
30705 setter = getSetter(controller.$name);
30706 setter(scope, controller);
30709 formElement.on('$destroy', function() {
30710 controller.$$parentForm.$removeControl(controller);
30711 setter(scope, undefined);
30712 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
30719 return formDirective;
30721 function getSetter(expression) {
30722 if (expression === '') {
30723 //create an assignable expression, so forms with an empty name can be renamed later
30724 return $parse('this[""]').assign;
30726 return $parse(expression).assign || noop;
30731 var formDirective = formDirectiveFactory();
30732 var ngFormDirective = formDirectiveFactory(true);
30734 /* global VALID_CLASS: false,
30735 INVALID_CLASS: false,
30736 PRISTINE_CLASS: false,
30737 DIRTY_CLASS: false,
30738 UNTOUCHED_CLASS: false,
30739 TOUCHED_CLASS: false,
30740 ngModelMinErr: false,
30743 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
30744 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)/;
30745 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
30746 // Note: We are being more lenient, because browsers are too.
30756 // 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
30757 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
30758 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;
30759 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
30760 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
30761 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
30762 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
30763 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
30764 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
30770 * @name input[text]
30773 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
30776 * @param {string} ngModel Assignable angular expression to data-bind to.
30777 * @param {string=} name Property name of the form under which the control is published.
30778 * @param {string=} required Adds `required` validation error key if the value is not entered.
30779 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30780 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30781 * `required` when you want to data-bind to the `required` attribute.
30782 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
30784 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
30785 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
30787 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
30788 * that contains the regular expression body that will be converted to a regular expression
30789 * as in the ngPattern directive.
30790 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
30791 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
30792 * If the expression evaluates to a RegExp object, then this is used directly.
30793 * If the expression evaluates to a string, then it will be converted to a RegExp
30794 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
30795 * `new RegExp('^abc$')`.<br />
30796 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30797 * start at the index of the last search's match, thus not taking the whole input value into
30799 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30800 * interaction with the input element.
30801 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
30802 * This parameter is ignored for input[type=password] controls, which will never trim the
30806 <example name="text-input-directive" module="textInputExample">
30807 <file name="index.html">
30809 angular.module('textInputExample', [])
30810 .controller('ExampleController', ['$scope', function($scope) {
30813 word: /^\s*\w*\s*$/
30817 <form name="myForm" ng-controller="ExampleController">
30818 <label>Single word:
30819 <input type="text" name="input" ng-model="example.text"
30820 ng-pattern="example.word" required ng-trim="false">
30823 <span class="error" ng-show="myForm.input.$error.required">
30825 <span class="error" ng-show="myForm.input.$error.pattern">
30826 Single word only!</span>
30828 <tt>text = {{example.text}}</tt><br/>
30829 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30830 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30831 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30832 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30835 <file name="protractor.js" type="protractor">
30836 var text = element(by.binding('example.text'));
30837 var valid = element(by.binding('myForm.input.$valid'));
30838 var input = element(by.model('example.text'));
30840 it('should initialize to model', function() {
30841 expect(text.getText()).toContain('guest');
30842 expect(valid.getText()).toContain('true');
30845 it('should be invalid if empty', function() {
30847 input.sendKeys('');
30849 expect(text.getText()).toEqual('text =');
30850 expect(valid.getText()).toContain('false');
30853 it('should be invalid if multi word', function() {
30855 input.sendKeys('hello world');
30857 expect(valid.getText()).toContain('false');
30862 'text': textInputType,
30866 * @name input[date]
30869 * Input with date validation and transformation. In browsers that do not yet support
30870 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
30871 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
30872 * modern browsers do not yet support this input type, it is important to provide cues to users on the
30873 * expected input format via a placeholder or label.
30875 * The model must always be a Date object, otherwise Angular will throw an error.
30876 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
30878 * The timezone to be used to read/write the `Date` instance in the model can be defined using
30879 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
30881 * @param {string} ngModel Assignable angular expression to data-bind to.
30882 * @param {string=} name Property name of the form under which the control is published.
30883 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
30884 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
30885 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
30886 * constraint validation.
30887 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
30888 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
30889 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
30890 * constraint validation.
30891 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
30892 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
30893 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
30894 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
30895 * @param {string=} required Sets `required` validation error key if the value is not entered.
30896 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30897 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30898 * `required` when you want to data-bind to the `required` attribute.
30899 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30900 * interaction with the input element.
30903 <example name="date-input-directive" module="dateInputExample">
30904 <file name="index.html">
30906 angular.module('dateInputExample', [])
30907 .controller('DateController', ['$scope', function($scope) {
30909 value: new Date(2013, 9, 22)
30913 <form name="myForm" ng-controller="DateController as dateCtrl">
30914 <label for="exampleInput">Pick a date in 2013:</label>
30915 <input type="date" id="exampleInput" name="input" ng-model="example.value"
30916 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
30918 <span class="error" ng-show="myForm.input.$error.required">
30920 <span class="error" ng-show="myForm.input.$error.date">
30921 Not a valid date!</span>
30923 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
30924 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
30925 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
30926 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30927 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30930 <file name="protractor.js" type="protractor">
30931 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
30932 var valid = element(by.binding('myForm.input.$valid'));
30933 var input = element(by.model('example.value'));
30935 // currently protractor/webdriver does not support
30936 // sending keys to all known HTML5 input controls
30937 // for various browsers (see https://github.com/angular/protractor/issues/562).
30938 function setInput(val) {
30939 // set the value of the element and force validation.
30940 var scr = "var ipt = document.getElementById('exampleInput'); " +
30941 "ipt.value = '" + val + "';" +
30942 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
30943 browser.executeScript(scr);
30946 it('should initialize to model', function() {
30947 expect(value.getText()).toContain('2013-10-22');
30948 expect(valid.getText()).toContain('myForm.input.$valid = true');
30951 it('should be invalid if empty', function() {
30953 expect(value.getText()).toEqual('value =');
30954 expect(valid.getText()).toContain('myForm.input.$valid = false');
30957 it('should be invalid if over max', function() {
30958 setInput('2015-01-01');
30959 expect(value.getText()).toContain('');
30960 expect(valid.getText()).toContain('myForm.input.$valid = false');
30965 'date': createDateInputType('date', DATE_REGEXP,
30966 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
30971 * @name input[datetime-local]
30974 * Input with datetime validation and transformation. In browsers that do not yet support
30975 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
30976 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
30978 * The model must always be a Date object, otherwise Angular will throw an error.
30979 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
30981 * The timezone to be used to read/write the `Date` instance in the model can be defined using
30982 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
30984 * @param {string} ngModel Assignable angular expression to data-bind to.
30985 * @param {string=} name Property name of the form under which the control is published.
30986 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
30987 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
30988 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
30989 * Note that `min` will also add native HTML5 constraint validation.
30990 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
30991 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
30992 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
30993 * Note that `max` will also add native HTML5 constraint validation.
30994 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
30995 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
30996 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
30997 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
30998 * @param {string=} required Sets `required` validation error key if the value is not entered.
30999 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31000 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31001 * `required` when you want to data-bind to the `required` attribute.
31002 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31003 * interaction with the input element.
31006 <example name="datetimelocal-input-directive" module="dateExample">
31007 <file name="index.html">
31009 angular.module('dateExample', [])
31010 .controller('DateController', ['$scope', function($scope) {
31012 value: new Date(2010, 11, 28, 14, 57)
31016 <form name="myForm" ng-controller="DateController as dateCtrl">
31017 <label for="exampleInput">Pick a date between in 2013:</label>
31018 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
31019 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
31021 <span class="error" ng-show="myForm.input.$error.required">
31023 <span class="error" ng-show="myForm.input.$error.datetimelocal">
31024 Not a valid date!</span>
31026 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
31027 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31028 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31029 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31030 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31033 <file name="protractor.js" type="protractor">
31034 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
31035 var valid = element(by.binding('myForm.input.$valid'));
31036 var input = element(by.model('example.value'));
31038 // currently protractor/webdriver does not support
31039 // sending keys to all known HTML5 input controls
31040 // for various browsers (https://github.com/angular/protractor/issues/562).
31041 function setInput(val) {
31042 // set the value of the element and force validation.
31043 var scr = "var ipt = document.getElementById('exampleInput'); " +
31044 "ipt.value = '" + val + "';" +
31045 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31046 browser.executeScript(scr);
31049 it('should initialize to model', function() {
31050 expect(value.getText()).toContain('2010-12-28T14:57:00');
31051 expect(valid.getText()).toContain('myForm.input.$valid = true');
31054 it('should be invalid if empty', function() {
31056 expect(value.getText()).toEqual('value =');
31057 expect(valid.getText()).toContain('myForm.input.$valid = false');
31060 it('should be invalid if over max', function() {
31061 setInput('2015-01-01T23:59:00');
31062 expect(value.getText()).toContain('');
31063 expect(valid.getText()).toContain('myForm.input.$valid = false');
31068 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
31069 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
31070 'yyyy-MM-ddTHH:mm:ss.sss'),
31074 * @name input[time]
31077 * Input with time validation and transformation. In browsers that do not yet support
31078 * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
31079 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
31080 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
31082 * The model must always be a Date object, otherwise Angular will throw an error.
31083 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31085 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31086 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31088 * @param {string} ngModel Assignable angular expression to data-bind to.
31089 * @param {string=} name Property name of the form under which the control is published.
31090 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31091 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
31092 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
31093 * native HTML5 constraint validation.
31094 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31095 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
31096 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
31097 * native HTML5 constraint validation.
31098 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
31099 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31100 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
31101 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31102 * @param {string=} required Sets `required` validation error key if the value is not entered.
31103 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31104 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31105 * `required` when you want to data-bind to the `required` attribute.
31106 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31107 * interaction with the input element.
31110 <example name="time-input-directive" module="timeExample">
31111 <file name="index.html">
31113 angular.module('timeExample', [])
31114 .controller('DateController', ['$scope', function($scope) {
31116 value: new Date(1970, 0, 1, 14, 57, 0)
31120 <form name="myForm" ng-controller="DateController as dateCtrl">
31121 <label for="exampleInput">Pick a between 8am and 5pm:</label>
31122 <input type="time" id="exampleInput" name="input" ng-model="example.value"
31123 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
31125 <span class="error" ng-show="myForm.input.$error.required">
31127 <span class="error" ng-show="myForm.input.$error.time">
31128 Not a valid date!</span>
31130 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
31131 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31132 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31133 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31134 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31137 <file name="protractor.js" type="protractor">
31138 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
31139 var valid = element(by.binding('myForm.input.$valid'));
31140 var input = element(by.model('example.value'));
31142 // currently protractor/webdriver does not support
31143 // sending keys to all known HTML5 input controls
31144 // for various browsers (https://github.com/angular/protractor/issues/562).
31145 function setInput(val) {
31146 // set the value of the element and force validation.
31147 var scr = "var ipt = document.getElementById('exampleInput'); " +
31148 "ipt.value = '" + val + "';" +
31149 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31150 browser.executeScript(scr);
31153 it('should initialize to model', function() {
31154 expect(value.getText()).toContain('14:57:00');
31155 expect(valid.getText()).toContain('myForm.input.$valid = true');
31158 it('should be invalid if empty', function() {
31160 expect(value.getText()).toEqual('value =');
31161 expect(valid.getText()).toContain('myForm.input.$valid = false');
31164 it('should be invalid if over max', function() {
31165 setInput('23:59:00');
31166 expect(value.getText()).toContain('');
31167 expect(valid.getText()).toContain('myForm.input.$valid = false');
31172 'time': createDateInputType('time', TIME_REGEXP,
31173 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
31178 * @name input[week]
31181 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
31182 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
31183 * week format (yyyy-W##), for example: `2013-W02`.
31185 * The model must always be a Date object, otherwise Angular will throw an error.
31186 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31188 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31189 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31191 * @param {string} ngModel Assignable angular expression to data-bind to.
31192 * @param {string=} name Property name of the form under which the control is published.
31193 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31194 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
31195 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
31196 * native HTML5 constraint validation.
31197 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31198 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
31199 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
31200 * native HTML5 constraint validation.
31201 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
31202 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31203 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
31204 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31205 * @param {string=} required Sets `required` validation error key if the value is not entered.
31206 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31207 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31208 * `required` when you want to data-bind to the `required` attribute.
31209 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31210 * interaction with the input element.
31213 <example name="week-input-directive" module="weekExample">
31214 <file name="index.html">
31216 angular.module('weekExample', [])
31217 .controller('DateController', ['$scope', function($scope) {
31219 value: new Date(2013, 0, 3)
31223 <form name="myForm" ng-controller="DateController as dateCtrl">
31224 <label>Pick a date between in 2013:
31225 <input id="exampleInput" type="week" name="input" ng-model="example.value"
31226 placeholder="YYYY-W##" min="2012-W32"
31227 max="2013-W52" required />
31230 <span class="error" ng-show="myForm.input.$error.required">
31232 <span class="error" ng-show="myForm.input.$error.week">
31233 Not a valid date!</span>
31235 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
31236 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31237 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31238 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31239 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31242 <file name="protractor.js" type="protractor">
31243 var value = element(by.binding('example.value | date: "yyyy-Www"'));
31244 var valid = element(by.binding('myForm.input.$valid'));
31245 var input = element(by.model('example.value'));
31247 // currently protractor/webdriver does not support
31248 // sending keys to all known HTML5 input controls
31249 // for various browsers (https://github.com/angular/protractor/issues/562).
31250 function setInput(val) {
31251 // set the value of the element and force validation.
31252 var scr = "var ipt = document.getElementById('exampleInput'); " +
31253 "ipt.value = '" + val + "';" +
31254 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31255 browser.executeScript(scr);
31258 it('should initialize to model', function() {
31259 expect(value.getText()).toContain('2013-W01');
31260 expect(valid.getText()).toContain('myForm.input.$valid = true');
31263 it('should be invalid if empty', function() {
31265 expect(value.getText()).toEqual('value =');
31266 expect(valid.getText()).toContain('myForm.input.$valid = false');
31269 it('should be invalid if over max', function() {
31270 setInput('2015-W01');
31271 expect(value.getText()).toContain('');
31272 expect(valid.getText()).toContain('myForm.input.$valid = false');
31277 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
31281 * @name input[month]
31284 * Input with month validation and transformation. In browsers that do not yet support
31285 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
31286 * month format (yyyy-MM), for example: `2009-01`.
31288 * The model must always be a Date object, otherwise Angular will throw an error.
31289 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
31290 * If the model is not set to the first of the month, the next view to model update will set it
31291 * to the first of the month.
31293 * The timezone to be used to read/write the `Date` instance in the model can be defined using
31294 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
31296 * @param {string} ngModel Assignable angular expression to data-bind to.
31297 * @param {string=} name Property name of the form under which the control is published.
31298 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31299 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
31300 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
31301 * native HTML5 constraint validation.
31302 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31303 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
31304 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
31305 * native HTML5 constraint validation.
31306 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
31307 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
31308 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
31309 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
31311 * @param {string=} required Sets `required` validation error key if the value is not entered.
31312 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31313 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31314 * `required` when you want to data-bind to the `required` attribute.
31315 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31316 * interaction with the input element.
31319 <example name="month-input-directive" module="monthExample">
31320 <file name="index.html">
31322 angular.module('monthExample', [])
31323 .controller('DateController', ['$scope', function($scope) {
31325 value: new Date(2013, 9, 1)
31329 <form name="myForm" ng-controller="DateController as dateCtrl">
31330 <label for="exampleInput">Pick a month in 2013:</label>
31331 <input id="exampleInput" type="month" name="input" ng-model="example.value"
31332 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
31334 <span class="error" ng-show="myForm.input.$error.required">
31336 <span class="error" ng-show="myForm.input.$error.month">
31337 Not a valid month!</span>
31339 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
31340 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31341 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31342 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31343 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31346 <file name="protractor.js" type="protractor">
31347 var value = element(by.binding('example.value | date: "yyyy-MM"'));
31348 var valid = element(by.binding('myForm.input.$valid'));
31349 var input = element(by.model('example.value'));
31351 // currently protractor/webdriver does not support
31352 // sending keys to all known HTML5 input controls
31353 // for various browsers (https://github.com/angular/protractor/issues/562).
31354 function setInput(val) {
31355 // set the value of the element and force validation.
31356 var scr = "var ipt = document.getElementById('exampleInput'); " +
31357 "ipt.value = '" + val + "';" +
31358 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
31359 browser.executeScript(scr);
31362 it('should initialize to model', function() {
31363 expect(value.getText()).toContain('2013-10');
31364 expect(valid.getText()).toContain('myForm.input.$valid = true');
31367 it('should be invalid if empty', function() {
31369 expect(value.getText()).toEqual('value =');
31370 expect(valid.getText()).toContain('myForm.input.$valid = false');
31373 it('should be invalid if over max', function() {
31374 setInput('2015-01');
31375 expect(value.getText()).toContain('');
31376 expect(valid.getText()).toContain('myForm.input.$valid = false');
31381 'month': createDateInputType('month', MONTH_REGEXP,
31382 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
31387 * @name input[number]
31390 * Text input with number validation and transformation. Sets the `number` validation
31391 * error if not a valid number.
31393 * <div class="alert alert-warning">
31394 * The model must always be of type `number` otherwise Angular will throw an error.
31395 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
31396 * error docs for more information and an example of how to convert your model if necessary.
31399 * ## Issues with HTML5 constraint validation
31401 * In browsers that follow the
31402 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
31403 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
31404 * If a non-number is entered in the input, the browser will report the value as an empty string,
31405 * which means the view / model values in `ngModel` and subsequently the scope value
31406 * will also be an empty string.
31409 * @param {string} ngModel Assignable angular expression to data-bind to.
31410 * @param {string=} name Property name of the form under which the control is published.
31411 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
31412 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
31413 * @param {string=} required Sets `required` validation error key if the value is not entered.
31414 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31415 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31416 * `required` when you want to data-bind to the `required` attribute.
31417 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31419 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31420 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
31422 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
31423 * that contains the regular expression body that will be converted to a regular expression
31424 * as in the ngPattern directive.
31425 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
31426 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
31427 * If the expression evaluates to a RegExp object, then this is used directly.
31428 * If the expression evaluates to a string, then it will be converted to a RegExp
31429 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31430 * `new RegExp('^abc$')`.<br />
31431 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31432 * start at the index of the last search's match, thus not taking the whole input value into
31434 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31435 * interaction with the input element.
31438 <example name="number-input-directive" module="numberExample">
31439 <file name="index.html">
31441 angular.module('numberExample', [])
31442 .controller('ExampleController', ['$scope', function($scope) {
31448 <form name="myForm" ng-controller="ExampleController">
31450 <input type="number" name="input" ng-model="example.value"
31451 min="0" max="99" required>
31454 <span class="error" ng-show="myForm.input.$error.required">
31456 <span class="error" ng-show="myForm.input.$error.number">
31457 Not valid number!</span>
31459 <tt>value = {{example.value}}</tt><br/>
31460 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31461 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31462 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31463 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31466 <file name="protractor.js" type="protractor">
31467 var value = element(by.binding('example.value'));
31468 var valid = element(by.binding('myForm.input.$valid'));
31469 var input = element(by.model('example.value'));
31471 it('should initialize to model', function() {
31472 expect(value.getText()).toContain('12');
31473 expect(valid.getText()).toContain('true');
31476 it('should be invalid if empty', function() {
31478 input.sendKeys('');
31479 expect(value.getText()).toEqual('value =');
31480 expect(valid.getText()).toContain('false');
31483 it('should be invalid if over max', function() {
31485 input.sendKeys('123');
31486 expect(value.getText()).toEqual('value =');
31487 expect(valid.getText()).toContain('false');
31492 'number': numberInputType,
31500 * Text input with URL validation. Sets the `url` validation error key if the content is not a
31503 * <div class="alert alert-warning">
31504 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
31505 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
31506 * the built-in validators (see the {@link guide/forms Forms guide})
31509 * @param {string} ngModel Assignable angular expression to data-bind to.
31510 * @param {string=} name Property name of the form under which the control is published.
31511 * @param {string=} required Sets `required` validation error key if the value is not entered.
31512 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31513 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31514 * `required` when you want to data-bind to the `required` attribute.
31515 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31517 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31518 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
31520 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
31521 * that contains the regular expression body that will be converted to a regular expression
31522 * as in the ngPattern directive.
31523 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
31524 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
31525 * If the expression evaluates to a RegExp object, then this is used directly.
31526 * If the expression evaluates to a string, then it will be converted to a RegExp
31527 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31528 * `new RegExp('^abc$')`.<br />
31529 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31530 * start at the index of the last search's match, thus not taking the whole input value into
31532 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31533 * interaction with the input element.
31536 <example name="url-input-directive" module="urlExample">
31537 <file name="index.html">
31539 angular.module('urlExample', [])
31540 .controller('ExampleController', ['$scope', function($scope) {
31542 text: 'http://google.com'
31546 <form name="myForm" ng-controller="ExampleController">
31548 <input type="url" name="input" ng-model="url.text" required>
31551 <span class="error" ng-show="myForm.input.$error.required">
31553 <span class="error" ng-show="myForm.input.$error.url">
31554 Not valid url!</span>
31556 <tt>text = {{url.text}}</tt><br/>
31557 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31558 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31559 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31560 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31561 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
31564 <file name="protractor.js" type="protractor">
31565 var text = element(by.binding('url.text'));
31566 var valid = element(by.binding('myForm.input.$valid'));
31567 var input = element(by.model('url.text'));
31569 it('should initialize to model', function() {
31570 expect(text.getText()).toContain('http://google.com');
31571 expect(valid.getText()).toContain('true');
31574 it('should be invalid if empty', function() {
31576 input.sendKeys('');
31578 expect(text.getText()).toEqual('text =');
31579 expect(valid.getText()).toContain('false');
31582 it('should be invalid if not url', function() {
31584 input.sendKeys('box');
31586 expect(valid.getText()).toContain('false');
31591 'url': urlInputType,
31596 * @name input[email]
31599 * Text input with email validation. Sets the `email` validation error key if not a valid email
31602 * <div class="alert alert-warning">
31603 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
31604 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
31605 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
31608 * @param {string} ngModel Assignable angular expression to data-bind to.
31609 * @param {string=} name Property name of the form under which the control is published.
31610 * @param {string=} required Sets `required` validation error key if the value is not entered.
31611 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
31612 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
31613 * `required` when you want to data-bind to the `required` attribute.
31614 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
31616 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
31617 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
31619 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
31620 * that contains the regular expression body that will be converted to a regular expression
31621 * as in the ngPattern directive.
31622 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
31623 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
31624 * If the expression evaluates to a RegExp object, then this is used directly.
31625 * If the expression evaluates to a string, then it will be converted to a RegExp
31626 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
31627 * `new RegExp('^abc$')`.<br />
31628 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
31629 * start at the index of the last search's match, thus not taking the whole input value into
31631 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31632 * interaction with the input element.
31635 <example name="email-input-directive" module="emailExample">
31636 <file name="index.html">
31638 angular.module('emailExample', [])
31639 .controller('ExampleController', ['$scope', function($scope) {
31641 text: 'me@example.com'
31645 <form name="myForm" ng-controller="ExampleController">
31647 <input type="email" name="input" ng-model="email.text" required>
31650 <span class="error" ng-show="myForm.input.$error.required">
31652 <span class="error" ng-show="myForm.input.$error.email">
31653 Not valid email!</span>
31655 <tt>text = {{email.text}}</tt><br/>
31656 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
31657 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
31658 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
31659 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
31660 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
31663 <file name="protractor.js" type="protractor">
31664 var text = element(by.binding('email.text'));
31665 var valid = element(by.binding('myForm.input.$valid'));
31666 var input = element(by.model('email.text'));
31668 it('should initialize to model', function() {
31669 expect(text.getText()).toContain('me@example.com');
31670 expect(valid.getText()).toContain('true');
31673 it('should be invalid if empty', function() {
31675 input.sendKeys('');
31676 expect(text.getText()).toEqual('text =');
31677 expect(valid.getText()).toContain('false');
31680 it('should be invalid if not email', function() {
31682 input.sendKeys('xxx');
31684 expect(valid.getText()).toContain('false');
31689 'email': emailInputType,
31694 * @name input[radio]
31697 * HTML radio button.
31699 * @param {string} ngModel Assignable angular expression to data-bind to.
31700 * @param {string} value The value to which the `ngModel` expression should be set when selected.
31701 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
31702 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
31703 * @param {string=} name Property name of the form under which the control is published.
31704 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31705 * interaction with the input element.
31706 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
31707 * is selected. Should be used instead of the `value` attribute if you need
31708 * a non-string `ngModel` (`boolean`, `array`, ...).
31711 <example name="radio-input-directive" module="radioExample">
31712 <file name="index.html">
31714 angular.module('radioExample', [])
31715 .controller('ExampleController', ['$scope', function($scope) {
31719 $scope.specialValue = {
31725 <form name="myForm" ng-controller="ExampleController">
31727 <input type="radio" ng-model="color.name" value="red">
31731 <input type="radio" ng-model="color.name" ng-value="specialValue">
31735 <input type="radio" ng-model="color.name" value="blue">
31738 <tt>color = {{color.name | json}}</tt><br/>
31740 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
31742 <file name="protractor.js" type="protractor">
31743 it('should change state', function() {
31744 var color = element(by.binding('color.name'));
31746 expect(color.getText()).toContain('blue');
31748 element.all(by.model('color.name')).get(0).click();
31750 expect(color.getText()).toContain('red');
31755 'radio': radioInputType,
31760 * @name input[checkbox]
31765 * @param {string} ngModel Assignable angular expression to data-bind to.
31766 * @param {string=} name Property name of the form under which the control is published.
31767 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
31768 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
31769 * @param {string=} ngChange Angular expression to be executed when input changes due to user
31770 * interaction with the input element.
31773 <example name="checkbox-input-directive" module="checkboxExample">
31774 <file name="index.html">
31776 angular.module('checkboxExample', [])
31777 .controller('ExampleController', ['$scope', function($scope) {
31778 $scope.checkboxModel = {
31784 <form name="myForm" ng-controller="ExampleController">
31786 <input type="checkbox" ng-model="checkboxModel.value1">
31789 <input type="checkbox" ng-model="checkboxModel.value2"
31790 ng-true-value="'YES'" ng-false-value="'NO'">
31792 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
31793 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
31796 <file name="protractor.js" type="protractor">
31797 it('should change state', function() {
31798 var value1 = element(by.binding('checkboxModel.value1'));
31799 var value2 = element(by.binding('checkboxModel.value2'));
31801 expect(value1.getText()).toContain('true');
31802 expect(value2.getText()).toContain('YES');
31804 element(by.model('checkboxModel.value1')).click();
31805 element(by.model('checkboxModel.value2')).click();
31807 expect(value1.getText()).toContain('false');
31808 expect(value2.getText()).toContain('NO');
31813 'checkbox': checkboxInputType,
31822 function stringBasedInputType(ctrl) {
31823 ctrl.$formatters.push(function(value) {
31824 return ctrl.$isEmpty(value) ? value : value.toString();
31828 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31829 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
31830 stringBasedInputType(ctrl);
31833 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31834 var type = lowercase(element[0].type);
31836 // In composition mode, users are still inputing intermediate text buffer,
31837 // hold the listener until composition is done.
31838 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
31839 if (!$sniffer.android) {
31840 var composing = false;
31842 element.on('compositionstart', function(data) {
31846 element.on('compositionend', function() {
31852 var listener = function(ev) {
31854 $browser.defer.cancel(timeout);
31857 if (composing) return;
31858 var value = element.val(),
31859 event = ev && ev.type;
31861 // By default we will trim the value
31862 // If the attribute ng-trim exists we will avoid trimming
31863 // If input type is 'password', the value is never trimmed
31864 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
31865 value = trim(value);
31868 // If a control is suffering from bad input (due to native validators), browsers discard its
31869 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
31870 // control's value is the same empty value twice in a row.
31871 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
31872 ctrl.$setViewValue(value, event);
31876 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
31877 // input event on backspace, delete or cut
31878 if ($sniffer.hasEvent('input')) {
31879 element.on('input', listener);
31883 var deferListener = function(ev, input, origValue) {
31885 timeout = $browser.defer(function() {
31887 if (!input || input.value !== origValue) {
31894 element.on('keydown', function(event) {
31895 var key = event.keyCode;
31898 // command modifiers arrows
31899 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
31901 deferListener(event, this, this.value);
31904 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
31905 if ($sniffer.hasEvent('paste')) {
31906 element.on('paste cut', deferListener);
31910 // if user paste into input using mouse on older browser
31911 // or form autocomplete on newer browser, we need "change" event to catch it
31912 element.on('change', listener);
31914 ctrl.$render = function() {
31915 // Workaround for Firefox validation #12102.
31916 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
31917 if (element.val() !== value) {
31918 element.val(value);
31923 function weekParser(isoWeek, existingDate) {
31924 if (isDate(isoWeek)) {
31928 if (isString(isoWeek)) {
31929 WEEK_REGEXP.lastIndex = 0;
31930 var parts = WEEK_REGEXP.exec(isoWeek);
31932 var year = +parts[1],
31938 firstThurs = getFirstThursdayOfYear(year),
31939 addDays = (week - 1) * 7;
31941 if (existingDate) {
31942 hours = existingDate.getHours();
31943 minutes = existingDate.getMinutes();
31944 seconds = existingDate.getSeconds();
31945 milliseconds = existingDate.getMilliseconds();
31948 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
31955 function createDateParser(regexp, mapping) {
31956 return function(iso, date) {
31963 if (isString(iso)) {
31964 // When a date is JSON'ified to wraps itself inside of an extra
31965 // set of double quotes. This makes the date parsing code unable
31966 // to match the date string and parse it as a date.
31967 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
31968 iso = iso.substring(1, iso.length - 1);
31970 if (ISO_DATE_REGEXP.test(iso)) {
31971 return new Date(iso);
31973 regexp.lastIndex = 0;
31974 parts = regexp.exec(iso);
31980 yyyy: date.getFullYear(),
31981 MM: date.getMonth() + 1,
31982 dd: date.getDate(),
31983 HH: date.getHours(),
31984 mm: date.getMinutes(),
31985 ss: date.getSeconds(),
31986 sss: date.getMilliseconds() / 1000
31989 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
31992 forEach(parts, function(part, index) {
31993 if (index < mapping.length) {
31994 map[mapping[index]] = +part;
31997 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
32005 function createDateInputType(type, regexp, parseDate, format) {
32006 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
32007 badInputChecker(scope, element, attr, ctrl);
32008 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32009 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
32012 ctrl.$$parserName = type;
32013 ctrl.$parsers.push(function(value) {
32014 if (ctrl.$isEmpty(value)) return null;
32015 if (regexp.test(value)) {
32016 // Note: We cannot read ctrl.$modelValue, as there might be a different
32017 // parser/formatter in the processing chain so that the model
32018 // contains some different data format!
32019 var parsedDate = parseDate(value, previousDate);
32021 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
32028 ctrl.$formatters.push(function(value) {
32029 if (value && !isDate(value)) {
32030 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
32032 if (isValidDate(value)) {
32033 previousDate = value;
32034 if (previousDate && timezone) {
32035 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
32037 return $filter('date')(value, format, timezone);
32039 previousDate = null;
32044 if (isDefined(attr.min) || attr.ngMin) {
32046 ctrl.$validators.min = function(value) {
32047 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
32049 attr.$observe('min', function(val) {
32050 minVal = parseObservedDateValue(val);
32055 if (isDefined(attr.max) || attr.ngMax) {
32057 ctrl.$validators.max = function(value) {
32058 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
32060 attr.$observe('max', function(val) {
32061 maxVal = parseObservedDateValue(val);
32066 function isValidDate(value) {
32067 // Invalid Date: getTime() returns NaN
32068 return value && !(value.getTime && value.getTime() !== value.getTime());
32071 function parseObservedDateValue(val) {
32072 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
32077 function badInputChecker(scope, element, attr, ctrl) {
32078 var node = element[0];
32079 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
32080 if (nativeValidation) {
32081 ctrl.$parsers.push(function(value) {
32082 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
32083 return validity.badInput || validity.typeMismatch ? undefined : value;
32088 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32089 badInputChecker(scope, element, attr, ctrl);
32090 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32092 ctrl.$$parserName = 'number';
32093 ctrl.$parsers.push(function(value) {
32094 if (ctrl.$isEmpty(value)) return null;
32095 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
32099 ctrl.$formatters.push(function(value) {
32100 if (!ctrl.$isEmpty(value)) {
32101 if (!isNumber(value)) {
32102 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
32104 value = value.toString();
32109 if (isDefined(attr.min) || attr.ngMin) {
32111 ctrl.$validators.min = function(value) {
32112 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
32115 attr.$observe('min', function(val) {
32116 if (isDefined(val) && !isNumber(val)) {
32117 val = parseFloat(val, 10);
32119 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
32120 // TODO(matsko): implement validateLater to reduce number of validations
32125 if (isDefined(attr.max) || attr.ngMax) {
32127 ctrl.$validators.max = function(value) {
32128 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
32131 attr.$observe('max', function(val) {
32132 if (isDefined(val) && !isNumber(val)) {
32133 val = parseFloat(val, 10);
32135 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
32136 // TODO(matsko): implement validateLater to reduce number of validations
32142 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32143 // Note: no badInputChecker here by purpose as `url` is only a validation
32144 // in browsers, i.e. we can always read out input.value even if it is not valid!
32145 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32146 stringBasedInputType(ctrl);
32148 ctrl.$$parserName = 'url';
32149 ctrl.$validators.url = function(modelValue, viewValue) {
32150 var value = modelValue || viewValue;
32151 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
32155 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
32156 // Note: no badInputChecker here by purpose as `url` is only a validation
32157 // in browsers, i.e. we can always read out input.value even if it is not valid!
32158 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
32159 stringBasedInputType(ctrl);
32161 ctrl.$$parserName = 'email';
32162 ctrl.$validators.email = function(modelValue, viewValue) {
32163 var value = modelValue || viewValue;
32164 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
32168 function radioInputType(scope, element, attr, ctrl) {
32169 // make the name unique, if not defined
32170 if (isUndefined(attr.name)) {
32171 element.attr('name', nextUid());
32174 var listener = function(ev) {
32175 if (element[0].checked) {
32176 ctrl.$setViewValue(attr.value, ev && ev.type);
32180 element.on('click', listener);
32182 ctrl.$render = function() {
32183 var value = attr.value;
32184 element[0].checked = (value == ctrl.$viewValue);
32187 attr.$observe('value', ctrl.$render);
32190 function parseConstantExpr($parse, context, name, expression, fallback) {
32192 if (isDefined(expression)) {
32193 parseFn = $parse(expression);
32194 if (!parseFn.constant) {
32195 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
32196 '`{1}`.', name, expression);
32198 return parseFn(context);
32203 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
32204 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
32205 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
32207 var listener = function(ev) {
32208 ctrl.$setViewValue(element[0].checked, ev && ev.type);
32211 element.on('click', listener);
32213 ctrl.$render = function() {
32214 element[0].checked = ctrl.$viewValue;
32217 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
32218 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
32219 // it to a boolean.
32220 ctrl.$isEmpty = function(value) {
32221 return value === false;
32224 ctrl.$formatters.push(function(value) {
32225 return equals(value, trueValue);
32228 ctrl.$parsers.push(function(value) {
32229 return value ? trueValue : falseValue;
32240 * HTML textarea element control with angular data-binding. The data-binding and validation
32241 * properties of this element are exactly the same as those of the
32242 * {@link ng.directive:input input element}.
32244 * @param {string} ngModel Assignable angular expression to data-bind to.
32245 * @param {string=} name Property name of the form under which the control is published.
32246 * @param {string=} required Sets `required` validation error key if the value is not entered.
32247 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
32248 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
32249 * `required` when you want to data-bind to the `required` attribute.
32250 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
32252 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
32253 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
32255 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
32256 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
32257 * If the expression evaluates to a RegExp object, then this is used directly.
32258 * If the expression evaluates to a string, then it will be converted to a RegExp
32259 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
32260 * `new RegExp('^abc$')`.<br />
32261 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32262 * start at the index of the last search's match, thus not taking the whole input value into
32264 * @param {string=} ngChange Angular expression to be executed when input changes due to user
32265 * interaction with the input element.
32266 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
32276 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
32277 * input state control, and validation.
32278 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
32280 * <div class="alert alert-warning">
32281 * **Note:** Not every feature offered is available for all input types.
32282 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
32285 * @param {string} ngModel Assignable angular expression to data-bind to.
32286 * @param {string=} name Property name of the form under which the control is published.
32287 * @param {string=} required Sets `required` validation error key if the value is not entered.
32288 * @param {boolean=} ngRequired Sets `required` attribute if set to true
32289 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
32291 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
32292 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
32294 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
32295 * value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
32296 * If the expression evaluates to a RegExp object, then this is used directly.
32297 * If the expression evaluates to a string, then it will be converted to a RegExp
32298 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
32299 * `new RegExp('^abc$')`.<br />
32300 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32301 * start at the index of the last search's match, thus not taking the whole input value into
32303 * @param {string=} ngChange Angular expression to be executed when input changes due to user
32304 * interaction with the input element.
32305 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
32306 * This parameter is ignored for input[type=password] controls, which will never trim the
32310 <example name="input-directive" module="inputExample">
32311 <file name="index.html">
32313 angular.module('inputExample', [])
32314 .controller('ExampleController', ['$scope', function($scope) {
32315 $scope.user = {name: 'guest', last: 'visitor'};
32318 <div ng-controller="ExampleController">
32319 <form name="myForm">
32322 <input type="text" name="userName" ng-model="user.name" required>
32325 <span class="error" ng-show="myForm.userName.$error.required">
32330 <input type="text" name="lastName" ng-model="user.last"
32331 ng-minlength="3" ng-maxlength="10">
32334 <span class="error" ng-show="myForm.lastName.$error.minlength">
32336 <span class="error" ng-show="myForm.lastName.$error.maxlength">
32341 <tt>user = {{user}}</tt><br/>
32342 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
32343 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
32344 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
32345 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
32346 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
32347 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
32348 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
32349 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
32352 <file name="protractor.js" type="protractor">
32353 var user = element(by.exactBinding('user'));
32354 var userNameValid = element(by.binding('myForm.userName.$valid'));
32355 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
32356 var lastNameError = element(by.binding('myForm.lastName.$error'));
32357 var formValid = element(by.binding('myForm.$valid'));
32358 var userNameInput = element(by.model('user.name'));
32359 var userLastInput = element(by.model('user.last'));
32361 it('should initialize to model', function() {
32362 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
32363 expect(userNameValid.getText()).toContain('true');
32364 expect(formValid.getText()).toContain('true');
32367 it('should be invalid if empty when required', function() {
32368 userNameInput.clear();
32369 userNameInput.sendKeys('');
32371 expect(user.getText()).toContain('{"last":"visitor"}');
32372 expect(userNameValid.getText()).toContain('false');
32373 expect(formValid.getText()).toContain('false');
32376 it('should be valid if empty when min length is set', function() {
32377 userLastInput.clear();
32378 userLastInput.sendKeys('');
32380 expect(user.getText()).toContain('{"name":"guest","last":""}');
32381 expect(lastNameValid.getText()).toContain('true');
32382 expect(formValid.getText()).toContain('true');
32385 it('should be invalid if less than required min length', function() {
32386 userLastInput.clear();
32387 userLastInput.sendKeys('xx');
32389 expect(user.getText()).toContain('{"name":"guest"}');
32390 expect(lastNameValid.getText()).toContain('false');
32391 expect(lastNameError.getText()).toContain('minlength');
32392 expect(formValid.getText()).toContain('false');
32395 it('should be invalid if longer than max length', function() {
32396 userLastInput.clear();
32397 userLastInput.sendKeys('some ridiculously long name');
32399 expect(user.getText()).toContain('{"name":"guest"}');
32400 expect(lastNameValid.getText()).toContain('false');
32401 expect(lastNameError.getText()).toContain('maxlength');
32402 expect(formValid.getText()).toContain('false');
32407 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
32408 function($browser, $sniffer, $filter, $parse) {
32411 require: ['?ngModel'],
32413 pre: function(scope, element, attr, ctrls) {
32415 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
32416 $browser, $filter, $parse);
32425 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
32431 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
32432 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
32435 * `ngValue` is useful when dynamically generating lists of radio buttons using
32436 * {@link ngRepeat `ngRepeat`}, as shown below.
32438 * Likewise, `ngValue` can be used to generate `<option>` elements for
32439 * the {@link select `select`} element. In that case however, only strings are supported
32440 * for the `value `attribute, so the resulting `ngModel` will always be a string.
32441 * Support for `select` models with non-string values is available via `ngOptions`.
32444 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
32445 * of the `input` element
32448 <example name="ngValue-directive" module="valueExample">
32449 <file name="index.html">
32451 angular.module('valueExample', [])
32452 .controller('ExampleController', ['$scope', function($scope) {
32453 $scope.names = ['pizza', 'unicorns', 'robots'];
32454 $scope.my = { favorite: 'unicorns' };
32457 <form ng-controller="ExampleController">
32458 <h2>Which is your favorite?</h2>
32459 <label ng-repeat="name in names" for="{{name}}">
32461 <input type="radio"
32462 ng-model="my.favorite"
32467 <div>You chose {{my.favorite}}</div>
32470 <file name="protractor.js" type="protractor">
32471 var favorite = element(by.binding('my.favorite'));
32473 it('should initialize to model', function() {
32474 expect(favorite.getText()).toContain('unicorns');
32476 it('should bind the values to the inputs', function() {
32477 element.all(by.model('my.favorite')).get(0).click();
32478 expect(favorite.getText()).toContain('pizza');
32483 var ngValueDirective = function() {
32487 compile: function(tpl, tplAttr) {
32488 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
32489 return function ngValueConstantLink(scope, elm, attr) {
32490 attr.$set('value', scope.$eval(attr.ngValue));
32493 return function ngValueLink(scope, elm, attr) {
32494 scope.$watch(attr.ngValue, function valueWatchAction(value) {
32495 attr.$set('value', value);
32509 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
32510 * with the value of a given expression, and to update the text content when the value of that
32511 * expression changes.
32513 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
32514 * `{{ expression }}` which is similar but less verbose.
32516 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
32517 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
32518 * element attribute, it makes the bindings invisible to the user while the page is loading.
32520 * An alternative solution to this problem would be using the
32521 * {@link ng.directive:ngCloak ngCloak} directive.
32525 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
32528 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
32529 <example module="bindExample">
32530 <file name="index.html">
32532 angular.module('bindExample', [])
32533 .controller('ExampleController', ['$scope', function($scope) {
32534 $scope.name = 'Whirled';
32537 <div ng-controller="ExampleController">
32538 <label>Enter name: <input type="text" ng-model="name"></label><br>
32539 Hello <span ng-bind="name"></span>!
32542 <file name="protractor.js" type="protractor">
32543 it('should check ng-bind', function() {
32544 var nameInput = element(by.model('name'));
32546 expect(element(by.binding('name')).getText()).toBe('Whirled');
32548 nameInput.sendKeys('world');
32549 expect(element(by.binding('name')).getText()).toBe('world');
32554 var ngBindDirective = ['$compile', function($compile) {
32557 compile: function ngBindCompile(templateElement) {
32558 $compile.$$addBindingClass(templateElement);
32559 return function ngBindLink(scope, element, attr) {
32560 $compile.$$addBindingInfo(element, attr.ngBind);
32561 element = element[0];
32562 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
32563 element.textContent = isUndefined(value) ? '' : value;
32573 * @name ngBindTemplate
32576 * The `ngBindTemplate` directive specifies that the element
32577 * text content should be replaced with the interpolation of the template
32578 * in the `ngBindTemplate` attribute.
32579 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
32580 * expressions. This directive is needed since some HTML elements
32581 * (such as TITLE and OPTION) cannot contain SPAN elements.
32584 * @param {string} ngBindTemplate template of form
32585 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
32588 * Try it here: enter text in text box and watch the greeting change.
32589 <example module="bindExample">
32590 <file name="index.html">
32592 angular.module('bindExample', [])
32593 .controller('ExampleController', ['$scope', function($scope) {
32594 $scope.salutation = 'Hello';
32595 $scope.name = 'World';
32598 <div ng-controller="ExampleController">
32599 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
32600 <label>Name: <input type="text" ng-model="name"></label><br>
32601 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
32604 <file name="protractor.js" type="protractor">
32605 it('should check ng-bind', function() {
32606 var salutationElem = element(by.binding('salutation'));
32607 var salutationInput = element(by.model('salutation'));
32608 var nameInput = element(by.model('name'));
32610 expect(salutationElem.getText()).toBe('Hello World!');
32612 salutationInput.clear();
32613 salutationInput.sendKeys('Greetings');
32615 nameInput.sendKeys('user');
32617 expect(salutationElem.getText()).toBe('Greetings user!');
32622 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
32624 compile: function ngBindTemplateCompile(templateElement) {
32625 $compile.$$addBindingClass(templateElement);
32626 return function ngBindTemplateLink(scope, element, attr) {
32627 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
32628 $compile.$$addBindingInfo(element, interpolateFn.expressions);
32629 element = element[0];
32630 attr.$observe('ngBindTemplate', function(value) {
32631 element.textContent = isUndefined(value) ? '' : value;
32644 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
32645 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
32646 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
32647 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
32648 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
32650 * You may also bypass sanitization for values you know are safe. To do so, bind to
32651 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
32652 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
32654 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
32655 * will have an exception (instead of an exploit.)
32658 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
32662 <example module="bindHtmlExample" deps="angular-sanitize.js">
32663 <file name="index.html">
32664 <div ng-controller="ExampleController">
32665 <p ng-bind-html="myHTML"></p>
32669 <file name="script.js">
32670 angular.module('bindHtmlExample', ['ngSanitize'])
32671 .controller('ExampleController', ['$scope', function($scope) {
32673 'I am an <code>HTML</code>string with ' +
32674 '<a href="#">links!</a> and other <em>stuff</em>';
32678 <file name="protractor.js" type="protractor">
32679 it('should check ng-bind-html', function() {
32680 expect(element(by.binding('myHTML')).getText()).toBe(
32681 'I am an HTMLstring with links! and other stuff');
32686 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
32689 compile: function ngBindHtmlCompile(tElement, tAttrs) {
32690 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
32691 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
32692 return (value || '').toString();
32694 $compile.$$addBindingClass(tElement);
32696 return function ngBindHtmlLink(scope, element, attr) {
32697 $compile.$$addBindingInfo(element, attr.ngBindHtml);
32699 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
32700 // we re-evaluate the expr because we want a TrustedValueHolderType
32701 // for $sce, not a string
32702 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
32714 * Evaluate the given expression when the user changes the input.
32715 * The expression is evaluated immediately, unlike the JavaScript onchange event
32716 * which only triggers at the end of a change (usually, when the user leaves the
32717 * form element or presses the return key).
32719 * The `ngChange` expression is only evaluated when a change in the input value causes
32720 * a new value to be committed to the model.
32722 * It will not be evaluated:
32723 * * if the value returned from the `$parsers` transformation pipeline has not changed
32724 * * if the input has continued to be invalid since the model will stay `null`
32725 * * if the model is changed programmatically and not by a change to the input value
32728 * Note, this directive requires `ngModel` to be present.
32731 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
32735 * <example name="ngChange-directive" module="changeExample">
32736 * <file name="index.html">
32738 * angular.module('changeExample', [])
32739 * .controller('ExampleController', ['$scope', function($scope) {
32740 * $scope.counter = 0;
32741 * $scope.change = function() {
32742 * $scope.counter++;
32746 * <div ng-controller="ExampleController">
32747 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
32748 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
32749 * <label for="ng-change-example2">Confirmed</label><br />
32750 * <tt>debug = {{confirmed}}</tt><br/>
32751 * <tt>counter = {{counter}}</tt><br/>
32754 * <file name="protractor.js" type="protractor">
32755 * var counter = element(by.binding('counter'));
32756 * var debug = element(by.binding('confirmed'));
32758 * it('should evaluate the expression if changing from view', function() {
32759 * expect(counter.getText()).toContain('0');
32761 * element(by.id('ng-change-example1')).click();
32763 * expect(counter.getText()).toContain('1');
32764 * expect(debug.getText()).toContain('true');
32767 * it('should not evaluate the expression if changing from model', function() {
32768 * element(by.id('ng-change-example2')).click();
32770 * expect(counter.getText()).toContain('0');
32771 * expect(debug.getText()).toContain('true');
32776 var ngChangeDirective = valueFn({
32778 require: 'ngModel',
32779 link: function(scope, element, attr, ctrl) {
32780 ctrl.$viewChangeListeners.push(function() {
32781 scope.$eval(attr.ngChange);
32786 function classDirective(name, selector) {
32787 name = 'ngClass' + name;
32788 return ['$animate', function($animate) {
32791 link: function(scope, element, attr) {
32794 scope.$watch(attr[name], ngClassWatchAction, true);
32796 attr.$observe('class', function(value) {
32797 ngClassWatchAction(scope.$eval(attr[name]));
32801 if (name !== 'ngClass') {
32802 scope.$watch('$index', function($index, old$index) {
32803 // jshint bitwise: false
32804 var mod = $index & 1;
32805 if (mod !== (old$index & 1)) {
32806 var classes = arrayClasses(scope.$eval(attr[name]));
32808 addClasses(classes) :
32809 removeClasses(classes);
32814 function addClasses(classes) {
32815 var newClasses = digestClassCounts(classes, 1);
32816 attr.$addClass(newClasses);
32819 function removeClasses(classes) {
32820 var newClasses = digestClassCounts(classes, -1);
32821 attr.$removeClass(newClasses);
32824 function digestClassCounts(classes, count) {
32825 // Use createMap() to prevent class assumptions involving property
32826 // names in Object.prototype
32827 var classCounts = element.data('$classCounts') || createMap();
32828 var classesToUpdate = [];
32829 forEach(classes, function(className) {
32830 if (count > 0 || classCounts[className]) {
32831 classCounts[className] = (classCounts[className] || 0) + count;
32832 if (classCounts[className] === +(count > 0)) {
32833 classesToUpdate.push(className);
32837 element.data('$classCounts', classCounts);
32838 return classesToUpdate.join(' ');
32841 function updateClasses(oldClasses, newClasses) {
32842 var toAdd = arrayDifference(newClasses, oldClasses);
32843 var toRemove = arrayDifference(oldClasses, newClasses);
32844 toAdd = digestClassCounts(toAdd, 1);
32845 toRemove = digestClassCounts(toRemove, -1);
32846 if (toAdd && toAdd.length) {
32847 $animate.addClass(element, toAdd);
32849 if (toRemove && toRemove.length) {
32850 $animate.removeClass(element, toRemove);
32854 function ngClassWatchAction(newVal) {
32855 if (selector === true || scope.$index % 2 === selector) {
32856 var newClasses = arrayClasses(newVal || []);
32858 addClasses(newClasses);
32859 } else if (!equals(newVal,oldVal)) {
32860 var oldClasses = arrayClasses(oldVal);
32861 updateClasses(oldClasses, newClasses);
32864 oldVal = shallowCopy(newVal);
32869 function arrayDifference(tokens1, tokens2) {
32873 for (var i = 0; i < tokens1.length; i++) {
32874 var token = tokens1[i];
32875 for (var j = 0; j < tokens2.length; j++) {
32876 if (token == tokens2[j]) continue outer;
32878 values.push(token);
32883 function arrayClasses(classVal) {
32885 if (isArray(classVal)) {
32886 forEach(classVal, function(v) {
32887 classes = classes.concat(arrayClasses(v));
32890 } else if (isString(classVal)) {
32891 return classVal.split(' ');
32892 } else if (isObject(classVal)) {
32893 forEach(classVal, function(v, k) {
32895 classes = classes.concat(k.split(' '));
32911 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
32912 * an expression that represents all classes to be added.
32914 * The directive operates in three different ways, depending on which of three types the expression
32917 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
32920 * 2. If the expression evaluates to an object, then for each key-value pair of the
32921 * object with a truthy value the corresponding key is used as a class name.
32923 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
32924 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
32925 * to give you more control over what CSS classes appear. See the code below for an example of this.
32928 * The directive won't add duplicate classes if a particular class was already set.
32930 * When the expression changes, the previously added classes are removed and only then are the
32931 * new classes added.
32934 * **add** - happens just before the class is applied to the elements
32936 * **remove** - happens just before the class is removed from the element
32939 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
32940 * of the evaluation can be a string representing space delimited class
32941 * names, an array, or a map of class names to boolean values. In the case of a map, the
32942 * names of the properties whose values are truthy will be added as css classes to the
32945 * @example Example that demonstrates basic bindings via ngClass directive.
32947 <file name="index.html">
32948 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
32950 <input type="checkbox" ng-model="deleted">
32951 deleted (apply "strike" class)
32954 <input type="checkbox" ng-model="important">
32955 important (apply "bold" class)
32958 <input type="checkbox" ng-model="error">
32959 error (apply "has-error" class)
32962 <p ng-class="style">Using String Syntax</p>
32963 <input type="text" ng-model="style"
32964 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
32966 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
32967 <input ng-model="style1"
32968 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
32969 <input ng-model="style2"
32970 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
32971 <input ng-model="style3"
32972 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
32974 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
32975 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
32976 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
32978 <file name="style.css">
32980 text-decoration: line-through;
32990 background-color: yellow;
32996 <file name="protractor.js" type="protractor">
32997 var ps = element.all(by.css('p'));
32999 it('should let you toggle the class', function() {
33001 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
33002 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
33004 element(by.model('important')).click();
33005 expect(ps.first().getAttribute('class')).toMatch(/bold/);
33007 element(by.model('error')).click();
33008 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
33011 it('should let you toggle string example', function() {
33012 expect(ps.get(1).getAttribute('class')).toBe('');
33013 element(by.model('style')).clear();
33014 element(by.model('style')).sendKeys('red');
33015 expect(ps.get(1).getAttribute('class')).toBe('red');
33018 it('array example should have 3 classes', function() {
33019 expect(ps.get(2).getAttribute('class')).toBe('');
33020 element(by.model('style1')).sendKeys('bold');
33021 element(by.model('style2')).sendKeys('strike');
33022 element(by.model('style3')).sendKeys('red');
33023 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
33026 it('array with map example should have 2 classes', function() {
33027 expect(ps.last().getAttribute('class')).toBe('');
33028 element(by.model('style4')).sendKeys('bold');
33029 element(by.model('warning')).click();
33030 expect(ps.last().getAttribute('class')).toBe('bold orange');
33037 The example below demonstrates how to perform animations using ngClass.
33039 <example module="ngAnimate" deps="angular-animate.js" animations="true">
33040 <file name="index.html">
33041 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
33042 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
33044 <span class="base-class" ng-class="myVar">Sample Text</span>
33046 <file name="style.css">
33048 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
33051 .base-class.my-class {
33056 <file name="protractor.js" type="protractor">
33057 it('should check ng-class', function() {
33058 expect(element(by.css('.base-class')).getAttribute('class')).not.
33059 toMatch(/my-class/);
33061 element(by.id('setbtn')).click();
33063 expect(element(by.css('.base-class')).getAttribute('class')).
33064 toMatch(/my-class/);
33066 element(by.id('clearbtn')).click();
33068 expect(element(by.css('.base-class')).getAttribute('class')).not.
33069 toMatch(/my-class/);
33075 ## ngClass and pre-existing CSS3 Transitions/Animations
33076 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
33077 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
33078 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
33079 to view the step by step details of {@link $animate#addClass $animate.addClass} and
33080 {@link $animate#removeClass $animate.removeClass}.
33082 var ngClassDirective = classDirective('', true);
33090 * The `ngClassOdd` and `ngClassEven` directives work exactly as
33091 * {@link ng.directive:ngClass ngClass}, except they work in
33092 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
33094 * This directive can be applied only within the scope of an
33095 * {@link ng.directive:ngRepeat ngRepeat}.
33098 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
33099 * of the evaluation can be a string representing space delimited class names or an array.
33103 <file name="index.html">
33104 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
33105 <li ng-repeat="name in names">
33106 <span ng-class-odd="'odd'" ng-class-even="'even'">
33112 <file name="style.css">
33120 <file name="protractor.js" type="protractor">
33121 it('should check ng-class-odd and ng-class-even', function() {
33122 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
33124 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
33130 var ngClassOddDirective = classDirective('Odd', 0);
33134 * @name ngClassEven
33138 * The `ngClassOdd` and `ngClassEven` directives work exactly as
33139 * {@link ng.directive:ngClass ngClass}, except they work in
33140 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
33142 * This directive can be applied only within the scope of an
33143 * {@link ng.directive:ngRepeat ngRepeat}.
33146 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
33147 * result of the evaluation can be a string representing space delimited class names or an array.
33151 <file name="index.html">
33152 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
33153 <li ng-repeat="name in names">
33154 <span ng-class-odd="'odd'" ng-class-even="'even'">
33155 {{name}}
33160 <file name="style.css">
33168 <file name="protractor.js" type="protractor">
33169 it('should check ng-class-odd and ng-class-even', function() {
33170 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
33172 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
33178 var ngClassEvenDirective = classDirective('Even', 1);
33186 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
33187 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
33188 * directive to avoid the undesirable flicker effect caused by the html template display.
33190 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
33191 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
33192 * of the browser view.
33194 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
33195 * `angular.min.js`.
33196 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
33199 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
33200 * display: none !important;
33204 * When this css rule is loaded by the browser, all html elements (including their children) that
33205 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
33206 * during the compilation of the template it deletes the `ngCloak` element attribute, making
33207 * the compiled element visible.
33209 * For the best result, the `angular.js` script must be loaded in the head section of the html
33210 * document; alternatively, the css rule above must be included in the external stylesheet of the
33217 <file name="index.html">
33218 <div id="template1" ng-cloak>{{ 'hello' }}</div>
33219 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
33221 <file name="protractor.js" type="protractor">
33222 it('should remove the template directive and css class', function() {
33223 expect($('#template1').getAttribute('ng-cloak')).
33225 expect($('#template2').getAttribute('ng-cloak')).
33232 var ngCloakDirective = ngDirective({
33233 compile: function(element, attr) {
33234 attr.$set('ngCloak', undefined);
33235 element.removeClass('ng-cloak');
33241 * @name ngController
33244 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
33245 * supports the principles behind the Model-View-Controller design pattern.
33247 * MVC components in angular:
33249 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
33250 * are accessed through bindings.
33251 * * View — The template (HTML with data bindings) that is rendered into the View.
33252 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
33253 * logic behind the application to decorate the scope with functions and values
33255 * Note that you can also attach controllers to the DOM by declaring it in a route definition
33256 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
33257 * again using `ng-controller` in the template itself. This will cause the controller to be attached
33258 * and executed twice.
33263 * @param {expression} ngController Name of a constructor function registered with the current
33264 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
33265 * that on the current scope evaluates to a constructor function.
33267 * The controller instance can be published into a scope property by specifying
33268 * `ng-controller="as propertyName"`.
33270 * If the current `$controllerProvider` is configured to use globals (via
33271 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
33272 * also be the name of a globally accessible constructor function (not recommended).
33275 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
33276 * greeting are methods declared on the controller (see source tab). These methods can
33277 * easily be called from the angular markup. Any changes to the data are automatically reflected
33278 * in the View without the need for a manual update.
33280 * Two different declaration styles are included below:
33282 * * one binds methods and properties directly onto the controller using `this`:
33283 * `ng-controller="SettingsController1 as settings"`
33284 * * one injects `$scope` into the controller:
33285 * `ng-controller="SettingsController2"`
33287 * The second option is more common in the Angular community, and is generally used in boilerplates
33288 * and in this guide. However, there are advantages to binding properties directly to the controller
33289 * and avoiding scope.
33291 * * Using `controller as` makes it obvious which controller you are accessing in the template when
33292 * multiple controllers apply to an element.
33293 * * If you are writing your controllers as classes you have easier access to the properties and
33294 * methods, which will appear on the scope, from inside the controller code.
33295 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
33296 * inheritance masking primitives.
33298 * This example demonstrates the `controller as` syntax.
33300 * <example name="ngControllerAs" module="controllerAsExample">
33301 * <file name="index.html">
33302 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
33303 * <label>Name: <input type="text" ng-model="settings.name"/></label>
33304 * <button ng-click="settings.greet()">greet</button><br/>
33307 * <li ng-repeat="contact in settings.contacts">
33308 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
33309 * <option>phone</option>
33310 * <option>email</option>
33312 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
33313 * <button ng-click="settings.clearContact(contact)">clear</button>
33314 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
33316 * <li><button ng-click="settings.addContact()">add</button></li>
33320 * <file name="app.js">
33321 * angular.module('controllerAsExample', [])
33322 * .controller('SettingsController1', SettingsController1);
33324 * function SettingsController1() {
33325 * this.name = "John Smith";
33326 * this.contacts = [
33327 * {type: 'phone', value: '408 555 1212'},
33328 * {type: 'email', value: 'john.smith@example.org'} ];
33331 * SettingsController1.prototype.greet = function() {
33332 * alert(this.name);
33335 * SettingsController1.prototype.addContact = function() {
33336 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
33339 * SettingsController1.prototype.removeContact = function(contactToRemove) {
33340 * var index = this.contacts.indexOf(contactToRemove);
33341 * this.contacts.splice(index, 1);
33344 * SettingsController1.prototype.clearContact = function(contact) {
33345 * contact.type = 'phone';
33346 * contact.value = '';
33349 * <file name="protractor.js" type="protractor">
33350 * it('should check controller as', function() {
33351 * var container = element(by.id('ctrl-as-exmpl'));
33352 * expect(container.element(by.model('settings.name'))
33353 * .getAttribute('value')).toBe('John Smith');
33355 * var firstRepeat =
33356 * container.element(by.repeater('contact in settings.contacts').row(0));
33357 * var secondRepeat =
33358 * container.element(by.repeater('contact in settings.contacts').row(1));
33360 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33361 * .toBe('408 555 1212');
33363 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
33364 * .toBe('john.smith@example.org');
33366 * firstRepeat.element(by.buttonText('clear')).click();
33368 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33371 * container.element(by.buttonText('add')).click();
33373 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
33374 * .element(by.model('contact.value'))
33375 * .getAttribute('value'))
33376 * .toBe('yourname@example.org');
33381 * This example demonstrates the "attach to `$scope`" style of controller.
33383 * <example name="ngController" module="controllerExample">
33384 * <file name="index.html">
33385 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
33386 * <label>Name: <input type="text" ng-model="name"/></label>
33387 * <button ng-click="greet()">greet</button><br/>
33390 * <li ng-repeat="contact in contacts">
33391 * <select ng-model="contact.type" id="select_{{$index}}">
33392 * <option>phone</option>
33393 * <option>email</option>
33395 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
33396 * <button ng-click="clearContact(contact)">clear</button>
33397 * <button ng-click="removeContact(contact)">X</button>
33399 * <li>[ <button ng-click="addContact()">add</button> ]</li>
33403 * <file name="app.js">
33404 * angular.module('controllerExample', [])
33405 * .controller('SettingsController2', ['$scope', SettingsController2]);
33407 * function SettingsController2($scope) {
33408 * $scope.name = "John Smith";
33409 * $scope.contacts = [
33410 * {type:'phone', value:'408 555 1212'},
33411 * {type:'email', value:'john.smith@example.org'} ];
33413 * $scope.greet = function() {
33414 * alert($scope.name);
33417 * $scope.addContact = function() {
33418 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
33421 * $scope.removeContact = function(contactToRemove) {
33422 * var index = $scope.contacts.indexOf(contactToRemove);
33423 * $scope.contacts.splice(index, 1);
33426 * $scope.clearContact = function(contact) {
33427 * contact.type = 'phone';
33428 * contact.value = '';
33432 * <file name="protractor.js" type="protractor">
33433 * it('should check controller', function() {
33434 * var container = element(by.id('ctrl-exmpl'));
33436 * expect(container.element(by.model('name'))
33437 * .getAttribute('value')).toBe('John Smith');
33439 * var firstRepeat =
33440 * container.element(by.repeater('contact in contacts').row(0));
33441 * var secondRepeat =
33442 * container.element(by.repeater('contact in contacts').row(1));
33444 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33445 * .toBe('408 555 1212');
33446 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
33447 * .toBe('john.smith@example.org');
33449 * firstRepeat.element(by.buttonText('clear')).click();
33451 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
33454 * container.element(by.buttonText('add')).click();
33456 * expect(container.element(by.repeater('contact in contacts').row(2))
33457 * .element(by.model('contact.value'))
33458 * .getAttribute('value'))
33459 * .toBe('yourname@example.org');
33465 var ngControllerDirective = [function() {
33481 * Angular has some features that can break certain
33482 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
33484 * If you intend to implement these rules then you must tell Angular not to use these features.
33486 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
33489 * The following rules affect Angular:
33491 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
33492 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
33493 * increase in the speed of evaluating Angular expressions.
33495 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
33496 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
33497 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
33498 * `angular-csp.css` in your HTML manually.
33500 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
33501 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
33502 * however, triggers a CSP error to be logged in the console:
33505 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
33506 * script in the following Content Security Policy directive: "default-src 'self'". Note that
33507 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
33510 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
33511 * directive on an element of the HTML document that appears before the `<script>` tag that loads
33512 * the `angular.js` file.
33514 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
33516 * You can specify which of the CSP related Angular features should be deactivated by providing
33517 * a value for the `ng-csp` attribute. The options are as follows:
33519 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
33521 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
33523 * You can use these values in the following combinations:
33526 * * No declaration means that Angular will assume that you can do inline styles, but it will do
33527 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
33530 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
33531 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
33534 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
33535 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
33537 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
33538 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
33540 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
33541 * styles nor use eval, which is the same as an empty: ng-csp.
33542 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
33545 * This example shows how to apply the `ngCsp` directive to the `html` tag.
33548 <html ng-app ng-csp>
33554 // Note: the suffix `.csp` in the example name triggers
33555 // csp mode in our http server!
33556 <example name="example.csp" module="cspExample" ng-csp="true">
33557 <file name="index.html">
33558 <div ng-controller="MainController as ctrl">
33560 <button ng-click="ctrl.inc()" id="inc">Increment</button>
33561 <span id="counter">
33567 <button ng-click="ctrl.evil()" id="evil">Evil</button>
33568 <span id="evilError">
33574 <file name="script.js">
33575 angular.module('cspExample', [])
33576 .controller('MainController', function() {
33578 this.inc = function() {
33581 this.evil = function() {
33582 // jshint evil:true
33586 this.evilError = e.message;
33591 <file name="protractor.js" type="protractor">
33592 var util, webdriver;
33594 var incBtn = element(by.id('inc'));
33595 var counter = element(by.id('counter'));
33596 var evilBtn = element(by.id('evil'));
33597 var evilError = element(by.id('evilError'));
33599 function getAndClearSevereErrors() {
33600 return browser.manage().logs().get('browser').then(function(browserLog) {
33601 return browserLog.filter(function(logEntry) {
33602 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
33607 function clearErrors() {
33608 getAndClearSevereErrors();
33611 function expectNoErrors() {
33612 getAndClearSevereErrors().then(function(filteredLog) {
33613 expect(filteredLog.length).toEqual(0);
33614 if (filteredLog.length) {
33615 console.log('browser console errors: ' + util.inspect(filteredLog));
33620 function expectError(regex) {
33621 getAndClearSevereErrors().then(function(filteredLog) {
33623 filteredLog.forEach(function(log) {
33624 if (log.message.match(regex)) {
33629 throw new Error('expected an error that matches ' + regex);
33634 beforeEach(function() {
33635 util = require('util');
33636 webdriver = require('protractor/node_modules/selenium-webdriver');
33639 // For now, we only test on Chrome,
33640 // as Safari does not load the page with Protractor's injected scripts,
33641 // and Firefox webdriver always disables content security policy (#6358)
33642 if (browser.params.browser !== 'chrome') {
33646 it('should not report errors when the page is loaded', function() {
33647 // clear errors so we are not dependent on previous tests
33649 // Need to reload the page as the page is already loaded when
33651 browser.driver.getCurrentUrl().then(function(url) {
33657 it('should evaluate expressions', function() {
33658 expect(counter.getText()).toEqual('0');
33660 expect(counter.getText()).toEqual('1');
33664 it('should throw and report an error when using "eval"', function() {
33666 expect(evilError.getText()).toMatch(/Content Security Policy/);
33667 expectError(/Content Security Policy/);
33673 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
33674 // bootstrap the system (before $parse is instantiated), for this reason we just have
33675 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
33682 * The ngClick directive allows you to specify custom behavior when
33683 * an element is clicked.
33687 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
33688 * click. ({@link guide/expression#-event- Event object is available as `$event`})
33692 <file name="index.html">
33693 <button ng-click="count = count + 1" ng-init="count=0">
33700 <file name="protractor.js" type="protractor">
33701 it('should check ng-click', function() {
33702 expect(element(by.binding('count')).getText()).toMatch('0');
33703 element(by.css('button')).click();
33704 expect(element(by.binding('count')).getText()).toMatch('1');
33710 * A collection of directives that allows creation of custom event handlers that are defined as
33711 * angular expressions and are compiled and executed within the current scope.
33713 var ngEventDirectives = {};
33715 // For events that might fire synchronously during DOM manipulation
33716 // we need to execute their event handlers asynchronously using $evalAsync,
33717 // so that they are not executed in an inconsistent state.
33718 var forceAsyncEvents = {
33723 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
33724 function(eventName) {
33725 var directiveName = directiveNormalize('ng-' + eventName);
33726 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
33729 compile: function($element, attr) {
33730 // We expose the powerful $event object on the scope that provides access to the Window,
33731 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
33732 // checks at the cost of speed since event handler expressions are not executed as
33733 // frequently as regular change detection.
33734 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
33735 return function ngEventHandler(scope, element) {
33736 element.on(eventName, function(event) {
33737 var callback = function() {
33738 fn(scope, {$event:event});
33740 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
33741 scope.$evalAsync(callback);
33743 scope.$apply(callback);
33758 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
33762 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
33763 * a dblclick. (The Event object is available as `$event`)
33767 <file name="index.html">
33768 <button ng-dblclick="count = count + 1" ng-init="count=0">
33769 Increment (on double click)
33779 * @name ngMousedown
33782 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
33786 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
33787 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
33791 <file name="index.html">
33792 <button ng-mousedown="count = count + 1" ng-init="count=0">
33793 Increment (on mouse down)
33806 * Specify custom behavior on mouseup event.
33810 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
33811 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
33815 <file name="index.html">
33816 <button ng-mouseup="count = count + 1" ng-init="count=0">
33817 Increment (on mouse up)
33826 * @name ngMouseover
33829 * Specify custom behavior on mouseover event.
33833 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
33834 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
33838 <file name="index.html">
33839 <button ng-mouseover="count = count + 1" ng-init="count=0">
33840 Increment (when mouse is over)
33850 * @name ngMouseenter
33853 * Specify custom behavior on mouseenter event.
33857 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
33858 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
33862 <file name="index.html">
33863 <button ng-mouseenter="count = count + 1" ng-init="count=0">
33864 Increment (when mouse enters)
33874 * @name ngMouseleave
33877 * Specify custom behavior on mouseleave event.
33881 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
33882 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
33886 <file name="index.html">
33887 <button ng-mouseleave="count = count + 1" ng-init="count=0">
33888 Increment (when mouse leaves)
33898 * @name ngMousemove
33901 * Specify custom behavior on mousemove event.
33905 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
33906 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
33910 <file name="index.html">
33911 <button ng-mousemove="count = count + 1" ng-init="count=0">
33912 Increment (when mouse moves)
33925 * Specify custom behavior on keydown event.
33929 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
33930 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
33934 <file name="index.html">
33935 <input ng-keydown="count = count + 1" ng-init="count=0">
33936 key down count: {{count}}
33947 * Specify custom behavior on keyup event.
33951 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
33952 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
33956 <file name="index.html">
33957 <p>Typing in the input box below updates the key count</p>
33958 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
33960 <p>Typing in the input box below updates the keycode</p>
33961 <input ng-keyup="event=$event">
33962 <p>event keyCode: {{ event.keyCode }}</p>
33963 <p>event altKey: {{ event.altKey }}</p>
33974 * Specify custom behavior on keypress event.
33977 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
33978 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
33979 * and can be interrogated for keyCode, altKey, etc.)
33983 <file name="index.html">
33984 <input ng-keypress="count = count + 1" ng-init="count=0">
33985 key press count: {{count}}
33996 * Enables binding angular expressions to onsubmit events.
33998 * Additionally it prevents the default action (which for form means sending the request to the
33999 * server and reloading the current page), but only if the form does not contain `action`,
34000 * `data-action`, or `x-action` attributes.
34002 * <div class="alert alert-warning">
34003 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
34004 * `ngSubmit` handlers together. See the
34005 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
34006 * for a detailed discussion of when `ngSubmit` may be triggered.
34011 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
34012 * ({@link guide/expression#-event- Event object is available as `$event`})
34015 <example module="submitExample">
34016 <file name="index.html">
34018 angular.module('submitExample', [])
34019 .controller('ExampleController', ['$scope', function($scope) {
34021 $scope.text = 'hello';
34022 $scope.submit = function() {
34024 $scope.list.push(this.text);
34030 <form ng-submit="submit()" ng-controller="ExampleController">
34031 Enter text and hit enter:
34032 <input type="text" ng-model="text" name="text" />
34033 <input type="submit" id="submit" value="Submit" />
34034 <pre>list={{list}}</pre>
34037 <file name="protractor.js" type="protractor">
34038 it('should check ng-submit', function() {
34039 expect(element(by.binding('list')).getText()).toBe('list=[]');
34040 element(by.css('#submit')).click();
34041 expect(element(by.binding('list')).getText()).toContain('hello');
34042 expect(element(by.model('text')).getAttribute('value')).toBe('');
34044 it('should ignore empty strings', function() {
34045 expect(element(by.binding('list')).getText()).toBe('list=[]');
34046 element(by.css('#submit')).click();
34047 element(by.css('#submit')).click();
34048 expect(element(by.binding('list')).getText()).toContain('hello');
34059 * Specify custom behavior on focus event.
34061 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
34062 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
34063 * during an `$apply` to ensure a consistent state.
34065 * @element window, input, select, textarea, a
34067 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
34068 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
34071 * See {@link ng.directive:ngClick ngClick}
34079 * Specify custom behavior on blur event.
34081 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
34082 * an element has lost focus.
34084 * Note: As the `blur` event is executed synchronously also during DOM manipulations
34085 * (e.g. removing a focussed input),
34086 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
34087 * during an `$apply` to ensure a consistent state.
34089 * @element window, input, select, textarea, a
34091 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
34092 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
34095 * See {@link ng.directive:ngClick ngClick}
34103 * Specify custom behavior on copy event.
34105 * @element window, input, select, textarea, a
34107 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
34108 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
34112 <file name="index.html">
34113 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
34124 * Specify custom behavior on cut event.
34126 * @element window, input, select, textarea, a
34128 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
34129 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
34133 <file name="index.html">
34134 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
34145 * Specify custom behavior on paste event.
34147 * @element window, input, select, textarea, a
34149 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
34150 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
34154 <file name="index.html">
34155 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
34168 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
34169 * {expression}. If the expression assigned to `ngIf` evaluates to a false
34170 * value then the element is removed from the DOM, otherwise a clone of the
34171 * element is reinserted into the DOM.
34173 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
34174 * element in the DOM rather than changing its visibility via the `display` css property. A common
34175 * case when this difference is significant is when using css selectors that rely on an element's
34176 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
34178 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
34179 * is created when the element is restored. The scope created within `ngIf` inherits from
34180 * its parent scope using
34181 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
34182 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
34183 * a javascript primitive defined in the parent scope. In this case any modifications made to the
34184 * variable within the child scope will override (hide) the value in the parent scope.
34186 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
34187 * is if an element's class attribute is directly modified after it's compiled, using something like
34188 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
34189 * the added class will be lost because the original compiled state is used to regenerate the element.
34191 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
34192 * and `leave` effects.
34195 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
34196 * leave - happens just before the `ngIf` contents are removed from the DOM
34201 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
34202 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
34203 * element is added to the DOM tree.
34206 <example module="ngAnimate" deps="angular-animate.js" animations="true">
34207 <file name="index.html">
34208 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
34210 <span ng-if="checked" class="animate-if">
34211 This is removed when the checkbox is unchecked.
34214 <file name="animations.css">
34217 border:1px solid black;
34221 .animate-if.ng-enter, .animate-if.ng-leave {
34222 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
34225 .animate-if.ng-enter,
34226 .animate-if.ng-leave.ng-leave-active {
34230 .animate-if.ng-leave,
34231 .animate-if.ng-enter.ng-enter-active {
34237 var ngIfDirective = ['$animate', function($animate) {
34239 multiElement: true,
34240 transclude: 'element',
34245 link: function($scope, $element, $attr, ctrl, $transclude) {
34246 var block, childScope, previousElements;
34247 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
34251 $transclude(function(clone, newScope) {
34252 childScope = newScope;
34253 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
34254 // Note: We only need the first/last node of the cloned nodes.
34255 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
34256 // by a directive with templateUrl when its template arrives.
34260 $animate.enter(clone, $element.parent(), $element);
34264 if (previousElements) {
34265 previousElements.remove();
34266 previousElements = null;
34269 childScope.$destroy();
34273 previousElements = getBlockNodes(block.clone);
34274 $animate.leave(previousElements).then(function() {
34275 previousElements = null;
34291 * Fetches, compiles and includes an external HTML fragment.
34293 * By default, the template URL is restricted to the same domain and protocol as the
34294 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
34295 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
34296 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
34297 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
34298 * ng.$sce Strict Contextual Escaping}.
34300 * In addition, the browser's
34301 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
34302 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
34303 * policy may further restrict whether the template is successfully loaded.
34304 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
34305 * access on some browsers.
34308 * enter - animation is used to bring new content into the browser.
34309 * leave - animation is used to animate existing content away.
34311 * The enter and leave animation occur concurrently.
34316 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
34317 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
34318 * @param {string=} onload Expression to evaluate when a new partial is loaded.
34319 * <div class="alert alert-warning">
34320 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
34321 * a function with the name on the window element, which will usually throw a
34322 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
34323 * different form that {@link guide/directive#normalization matches} `onload`.
34326 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
34327 * $anchorScroll} to scroll the viewport after the content is loaded.
34329 * - If the attribute is not set, disable scrolling.
34330 * - If the attribute is set without value, enable scrolling.
34331 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
34334 <example module="includeExample" deps="angular-animate.js" animations="true">
34335 <file name="index.html">
34336 <div ng-controller="ExampleController">
34337 <select ng-model="template" ng-options="t.name for t in templates">
34338 <option value="">(blank)</option>
34340 url of the template: <code>{{template.url}}</code>
34342 <div class="slide-animate-container">
34343 <div class="slide-animate" ng-include="template.url"></div>
34347 <file name="script.js">
34348 angular.module('includeExample', ['ngAnimate'])
34349 .controller('ExampleController', ['$scope', function($scope) {
34351 [ { name: 'template1.html', url: 'template1.html'},
34352 { name: 'template2.html', url: 'template2.html'} ];
34353 $scope.template = $scope.templates[0];
34356 <file name="template1.html">
34357 Content of template1.html
34359 <file name="template2.html">
34360 Content of template2.html
34362 <file name="animations.css">
34363 .slide-animate-container {
34366 border:1px solid black;
34375 .slide-animate.ng-enter, .slide-animate.ng-leave {
34376 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
34387 .slide-animate.ng-enter {
34390 .slide-animate.ng-enter.ng-enter-active {
34394 .slide-animate.ng-leave {
34397 .slide-animate.ng-leave.ng-leave-active {
34401 <file name="protractor.js" type="protractor">
34402 var templateSelect = element(by.model('template'));
34403 var includeElem = element(by.css('[ng-include]'));
34405 it('should load template1.html', function() {
34406 expect(includeElem.getText()).toMatch(/Content of template1.html/);
34409 it('should load template2.html', function() {
34410 if (browser.params.browser == 'firefox') {
34411 // Firefox can't handle using selects
34412 // See https://github.com/angular/protractor/issues/480
34415 templateSelect.click();
34416 templateSelect.all(by.css('option')).get(2).click();
34417 expect(includeElem.getText()).toMatch(/Content of template2.html/);
34420 it('should change to blank', function() {
34421 if (browser.params.browser == 'firefox') {
34422 // Firefox can't handle using selects
34425 templateSelect.click();
34426 templateSelect.all(by.css('option')).get(0).click();
34427 expect(includeElem.isPresent()).toBe(false);
34436 * @name ngInclude#$includeContentRequested
34437 * @eventType emit on the scope ngInclude was declared in
34439 * Emitted every time the ngInclude content is requested.
34441 * @param {Object} angularEvent Synthetic event object.
34442 * @param {String} src URL of content to load.
34448 * @name ngInclude#$includeContentLoaded
34449 * @eventType emit on the current ngInclude scope
34451 * Emitted every time the ngInclude content is reloaded.
34453 * @param {Object} angularEvent Synthetic event object.
34454 * @param {String} src URL of content to load.
34460 * @name ngInclude#$includeContentError
34461 * @eventType emit on the scope ngInclude was declared in
34463 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
34465 * @param {Object} angularEvent Synthetic event object.
34466 * @param {String} src URL of content to load.
34468 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
34469 function($templateRequest, $anchorScroll, $animate) {
34474 transclude: 'element',
34475 controller: angular.noop,
34476 compile: function(element, attr) {
34477 var srcExp = attr.ngInclude || attr.src,
34478 onloadExp = attr.onload || '',
34479 autoScrollExp = attr.autoscroll;
34481 return function(scope, $element, $attr, ctrl, $transclude) {
34482 var changeCounter = 0,
34487 var cleanupLastIncludeContent = function() {
34488 if (previousElement) {
34489 previousElement.remove();
34490 previousElement = null;
34492 if (currentScope) {
34493 currentScope.$destroy();
34494 currentScope = null;
34496 if (currentElement) {
34497 $animate.leave(currentElement).then(function() {
34498 previousElement = null;
34500 previousElement = currentElement;
34501 currentElement = null;
34505 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
34506 var afterAnimation = function() {
34507 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
34511 var thisChangeId = ++changeCounter;
34514 //set the 2nd param to true to ignore the template request error so that the inner
34515 //contents and scope can be cleaned up.
34516 $templateRequest(src, true).then(function(response) {
34517 if (scope.$$destroyed) return;
34519 if (thisChangeId !== changeCounter) return;
34520 var newScope = scope.$new();
34521 ctrl.template = response;
34523 // Note: This will also link all children of ng-include that were contained in the original
34524 // html. If that content contains controllers, ... they could pollute/change the scope.
34525 // However, using ng-include on an element with additional content does not make sense...
34526 // Note: We can't remove them in the cloneAttchFn of $transclude as that
34527 // function is called before linking the content, which would apply child
34528 // directives to non existing elements.
34529 var clone = $transclude(newScope, function(clone) {
34530 cleanupLastIncludeContent();
34531 $animate.enter(clone, null, $element).then(afterAnimation);
34534 currentScope = newScope;
34535 currentElement = clone;
34537 currentScope.$emit('$includeContentLoaded', src);
34538 scope.$eval(onloadExp);
34540 if (scope.$$destroyed) return;
34542 if (thisChangeId === changeCounter) {
34543 cleanupLastIncludeContent();
34544 scope.$emit('$includeContentError', src);
34547 scope.$emit('$includeContentRequested', src);
34549 cleanupLastIncludeContent();
34550 ctrl.template = null;
34558 // This directive is called during the $transclude call of the first `ngInclude` directive.
34559 // It will replace and compile the content of the element with the loaded template.
34560 // We need this directive so that the element content is already filled when
34561 // the link function of another directive on the same element as ngInclude
34563 var ngIncludeFillContentDirective = ['$compile',
34564 function($compile) {
34568 require: 'ngInclude',
34569 link: function(scope, $element, $attr, ctrl) {
34570 if (toString.call($element[0]).match(/SVG/)) {
34571 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
34572 // support innerHTML, so detect this here and try to generate the contents
34575 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
34576 function namespaceAdaptedClone(clone) {
34577 $element.append(clone);
34578 }, {futureParentElement: $element});
34582 $element.html(ctrl.template);
34583 $compile($element.contents())(scope);
34594 * The `ngInit` directive allows you to evaluate an expression in the
34597 * <div class="alert alert-danger">
34598 * This directive can be abused to add unnecessary amounts of logic into your templates.
34599 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
34600 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
34601 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
34602 * rather than `ngInit` to initialize values on a scope.
34605 * <div class="alert alert-warning">
34606 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
34607 * sure you have parentheses to ensure correct operator precedence:
34608 * <pre class="prettyprint">
34609 * `<div ng-init="test1 = ($index | toString)"></div>`
34616 * @param {expression} ngInit {@link guide/expression Expression} to eval.
34619 <example module="initExample">
34620 <file name="index.html">
34622 angular.module('initExample', [])
34623 .controller('ExampleController', ['$scope', function($scope) {
34624 $scope.list = [['a', 'b'], ['c', 'd']];
34627 <div ng-controller="ExampleController">
34628 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
34629 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
34630 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
34635 <file name="protractor.js" type="protractor">
34636 it('should alias index positions', function() {
34637 var elements = element.all(by.css('.example-init'));
34638 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
34639 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
34640 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
34641 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
34646 var ngInitDirective = ngDirective({
34648 compile: function() {
34650 pre: function(scope, element, attrs) {
34651 scope.$eval(attrs.ngInit);
34662 * Text input that converts between a delimited string and an array of strings. The default
34663 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
34664 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
34666 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
34667 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
34668 * list item is respected. This implies that the user of the directive is responsible for
34669 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
34670 * tab or newline character.
34671 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
34672 * when joining the list items back together) and whitespace around each list item is stripped
34673 * before it is added to the model.
34675 * ### Example with Validation
34677 * <example name="ngList-directive" module="listExample">
34678 * <file name="app.js">
34679 * angular.module('listExample', [])
34680 * .controller('ExampleController', ['$scope', function($scope) {
34681 * $scope.names = ['morpheus', 'neo', 'trinity'];
34684 * <file name="index.html">
34685 * <form name="myForm" ng-controller="ExampleController">
34686 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
34687 * <span role="alert">
34688 * <span class="error" ng-show="myForm.namesInput.$error.required">
34692 * <tt>names = {{names}}</tt><br/>
34693 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
34694 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
34695 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
34696 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
34699 * <file name="protractor.js" type="protractor">
34700 * var listInput = element(by.model('names'));
34701 * var names = element(by.exactBinding('names'));
34702 * var valid = element(by.binding('myForm.namesInput.$valid'));
34703 * var error = element(by.css('span.error'));
34705 * it('should initialize to model', function() {
34706 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
34707 * expect(valid.getText()).toContain('true');
34708 * expect(error.getCssValue('display')).toBe('none');
34711 * it('should be invalid if empty', function() {
34712 * listInput.clear();
34713 * listInput.sendKeys('');
34715 * expect(names.getText()).toContain('');
34716 * expect(valid.getText()).toContain('false');
34717 * expect(error.getCssValue('display')).not.toBe('none');
34722 * ### Example - splitting on newline
34723 * <example name="ngList-directive-newlines">
34724 * <file name="index.html">
34725 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
34726 * <pre>{{ list | json }}</pre>
34728 * <file name="protractor.js" type="protractor">
34729 * it("should split the text by newlines", function() {
34730 * var listInput = element(by.model('list'));
34731 * var output = element(by.binding('list | json'));
34732 * listInput.sendKeys('abc\ndef\nghi');
34733 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
34739 * @param {string=} ngList optional delimiter that should be used to split the value.
34741 var ngListDirective = function() {
34745 require: 'ngModel',
34746 link: function(scope, element, attr, ctrl) {
34747 // We want to control whitespace trimming so we use this convoluted approach
34748 // to access the ngList attribute, which doesn't pre-trim the attribute
34749 var ngList = element.attr(attr.$attr.ngList) || ', ';
34750 var trimValues = attr.ngTrim !== 'false';
34751 var separator = trimValues ? trim(ngList) : ngList;
34753 var parse = function(viewValue) {
34754 // If the viewValue is invalid (say required but empty) it will be `undefined`
34755 if (isUndefined(viewValue)) return;
34760 forEach(viewValue.split(separator), function(value) {
34761 if (value) list.push(trimValues ? trim(value) : value);
34768 ctrl.$parsers.push(parse);
34769 ctrl.$formatters.push(function(value) {
34770 if (isArray(value)) {
34771 return value.join(ngList);
34777 // Override the standard $isEmpty because an empty array means the input is empty.
34778 ctrl.$isEmpty = function(value) {
34779 return !value || !value.length;
34785 /* global VALID_CLASS: true,
34786 INVALID_CLASS: true,
34787 PRISTINE_CLASS: true,
34789 UNTOUCHED_CLASS: true,
34790 TOUCHED_CLASS: true,
34793 var VALID_CLASS = 'ng-valid',
34794 INVALID_CLASS = 'ng-invalid',
34795 PRISTINE_CLASS = 'ng-pristine',
34796 DIRTY_CLASS = 'ng-dirty',
34797 UNTOUCHED_CLASS = 'ng-untouched',
34798 TOUCHED_CLASS = 'ng-touched',
34799 PENDING_CLASS = 'ng-pending',
34800 EMPTY_CLASS = 'ng-empty',
34801 NOT_EMPTY_CLASS = 'ng-not-empty';
34803 var ngModelMinErr = minErr('ngModel');
34807 * @name ngModel.NgModelController
34809 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
34810 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
34812 * @property {*} $modelValue The value in the model that the control is bound to.
34813 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
34814 the control reads value from the DOM. The functions are called in array order, each passing
34815 its return value through to the next. The last return value is forwarded to the
34816 {@link ngModel.NgModelController#$validators `$validators`} collection.
34818 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
34821 Returning `undefined` from a parser means a parse error occurred. In that case,
34822 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
34823 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
34824 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
34827 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
34828 the model value changes. The functions are called in reverse array order, each passing the value through to the
34829 next. The last return value is used as the actual DOM value.
34830 Used to format / convert values for display in the control.
34832 * function formatter(value) {
34834 * return value.toUpperCase();
34837 * ngModel.$formatters.push(formatter);
34840 * @property {Object.<string, function>} $validators A collection of validators that are applied
34841 * whenever the model value changes. The key value within the object refers to the name of the
34842 * validator while the function refers to the validation operation. The validation operation is
34843 * provided with the model value as an argument and must return a true or false value depending
34844 * on the response of that validation.
34847 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
34848 * var value = modelValue || viewValue;
34849 * return /[0-9]+/.test(value) &&
34850 * /[a-z]+/.test(value) &&
34851 * /[A-Z]+/.test(value) &&
34852 * /\W+/.test(value);
34856 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
34857 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
34858 * is expected to return a promise when it is run during the model validation process. Once the promise
34859 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
34860 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
34861 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
34862 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
34863 * will only run once all synchronous validators have passed.
34865 * Please note that if $http is used then it is important that the server returns a success HTTP response code
34866 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
34869 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
34870 * var value = modelValue || viewValue;
34872 * // Lookup user by username
34873 * return $http.get('/api/users/' + value).
34874 * then(function resolved() {
34875 * //username exists, this means validation fails
34876 * return $q.reject('exists');
34877 * }, function rejected() {
34878 * //username does not exist, therefore this validation passes
34884 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
34885 * view value has changed. It is called with no arguments, and its return value is ignored.
34886 * This can be used in place of additional $watches against the model value.
34888 * @property {Object} $error An object hash with all failing validator ids as keys.
34889 * @property {Object} $pending An object hash with all pending validator ids as keys.
34891 * @property {boolean} $untouched True if control has not lost focus yet.
34892 * @property {boolean} $touched True if control has lost focus.
34893 * @property {boolean} $pristine True if user has not interacted with the control yet.
34894 * @property {boolean} $dirty True if user has already interacted with the control.
34895 * @property {boolean} $valid True if there is no error.
34896 * @property {boolean} $invalid True if at least one error on the control.
34897 * @property {string} $name The name attribute of the control.
34901 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
34902 * The controller contains services for data-binding, validation, CSS updates, and value formatting
34903 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
34904 * listening to DOM events.
34905 * Such DOM related logic should be provided by other directives which make use of
34906 * `NgModelController` for data-binding to control elements.
34907 * Angular provides this DOM logic for most {@link input `input`} elements.
34908 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
34909 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
34912 * ### Custom Control Example
34913 * This example shows how to use `NgModelController` with a custom control to achieve
34914 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
34915 * collaborate together to achieve the desired result.
34917 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
34918 * contents be edited in place by the user.
34920 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
34921 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
34922 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
34923 * that content using the `$sce` service.
34925 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
34926 <file name="style.css">
34927 [contenteditable] {
34928 border: 1px solid black;
34929 background-color: white;
34934 border: 1px solid red;
34938 <file name="script.js">
34939 angular.module('customControl', ['ngSanitize']).
34940 directive('contenteditable', ['$sce', function($sce) {
34942 restrict: 'A', // only activate on element attribute
34943 require: '?ngModel', // get a hold of NgModelController
34944 link: function(scope, element, attrs, ngModel) {
34945 if (!ngModel) return; // do nothing if no ng-model
34947 // Specify how UI should be updated
34948 ngModel.$render = function() {
34949 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
34952 // Listen for change events to enable binding
34953 element.on('blur keyup change', function() {
34954 scope.$evalAsync(read);
34956 read(); // initialize
34958 // Write data to the model
34960 var html = element.html();
34961 // When we clear the content editable the browser leaves a <br> behind
34962 // If strip-br attribute is provided then we strip this out
34963 if ( attrs.stripBr && html == '<br>' ) {
34966 ngModel.$setViewValue(html);
34972 <file name="index.html">
34973 <form name="myForm">
34974 <div contenteditable
34975 name="myWidget" ng-model="userContent"
34977 required>Change me!</div>
34978 <span ng-show="myForm.myWidget.$error.required">Required!</span>
34980 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
34983 <file name="protractor.js" type="protractor">
34984 it('should data-bind and become invalid', function() {
34985 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
34986 // SafariDriver can't handle contenteditable
34987 // and Firefox driver can't clear contenteditables very well
34990 var contentEditable = element(by.css('[contenteditable]'));
34991 var content = 'Change me!';
34993 expect(contentEditable.getText()).toEqual(content);
34995 contentEditable.clear();
34996 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
34997 expect(contentEditable.getText()).toEqual('');
34998 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
35005 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
35006 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
35007 this.$viewValue = Number.NaN;
35008 this.$modelValue = Number.NaN;
35009 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
35010 this.$validators = {};
35011 this.$asyncValidators = {};
35012 this.$parsers = [];
35013 this.$formatters = [];
35014 this.$viewChangeListeners = [];
35015 this.$untouched = true;
35016 this.$touched = false;
35017 this.$pristine = true;
35018 this.$dirty = false;
35019 this.$valid = true;
35020 this.$invalid = false;
35021 this.$error = {}; // keep invalid keys here
35022 this.$$success = {}; // keep valid keys here
35023 this.$pending = undefined; // keep pending keys here
35024 this.$name = $interpolate($attr.name || '', false)($scope);
35025 this.$$parentForm = nullFormCtrl;
35027 var parsedNgModel = $parse($attr.ngModel),
35028 parsedNgModelAssign = parsedNgModel.assign,
35029 ngModelGet = parsedNgModel,
35030 ngModelSet = parsedNgModelAssign,
35031 pendingDebounce = null,
35035 this.$$setOptions = function(options) {
35036 ctrl.$options = options;
35037 if (options && options.getterSetter) {
35038 var invokeModelGetter = $parse($attr.ngModel + '()'),
35039 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
35041 ngModelGet = function($scope) {
35042 var modelValue = parsedNgModel($scope);
35043 if (isFunction(modelValue)) {
35044 modelValue = invokeModelGetter($scope);
35048 ngModelSet = function($scope, newValue) {
35049 if (isFunction(parsedNgModel($scope))) {
35050 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
35052 parsedNgModelAssign($scope, ctrl.$modelValue);
35055 } else if (!parsedNgModel.assign) {
35056 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
35057 $attr.ngModel, startingTag($element));
35063 * @name ngModel.NgModelController#$render
35066 * Called when the view needs to be updated. It is expected that the user of the ng-model
35067 * directive will implement this method.
35069 * The `$render()` method is invoked in the following situations:
35071 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
35072 * committed value then `$render()` is called to update the input control.
35073 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
35074 * the `$viewValue` are different from last time.
35076 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
35077 * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
35078 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
35079 * invoked if you only change a property on the objects.
35081 this.$render = noop;
35085 * @name ngModel.NgModelController#$isEmpty
35088 * This is called when we need to determine if the value of an input is empty.
35090 * For instance, the required directive does this to work out if the input has data or not.
35092 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
35094 * You can override this for input directives whose concept of being empty is different from the
35095 * default. The `checkboxInputType` directive does this because in its case a value of `false`
35098 * @param {*} value The value of the input to check for emptiness.
35099 * @returns {boolean} True if `value` is "empty".
35101 this.$isEmpty = function(value) {
35102 return isUndefined(value) || value === '' || value === null || value !== value;
35105 this.$$updateEmptyClasses = function(value) {
35106 if (ctrl.$isEmpty(value)) {
35107 $animate.removeClass($element, NOT_EMPTY_CLASS);
35108 $animate.addClass($element, EMPTY_CLASS);
35110 $animate.removeClass($element, EMPTY_CLASS);
35111 $animate.addClass($element, NOT_EMPTY_CLASS);
35116 var currentValidationRunId = 0;
35120 * @name ngModel.NgModelController#$setValidity
35123 * Change the validity state, and notify the form.
35125 * This method can be called within $parsers/$formatters or a custom validation implementation.
35126 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
35127 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
35129 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
35130 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
35131 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
35132 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
35133 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
35134 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
35135 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
35136 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
35137 * Skipped is used by Angular when validators do not run because of parse errors and
35138 * when `$asyncValidators` do not run because any of the `$validators` failed.
35140 addSetValidityMethod({
35142 $element: $element,
35143 set: function(object, property) {
35144 object[property] = true;
35146 unset: function(object, property) {
35147 delete object[property];
35154 * @name ngModel.NgModelController#$setPristine
35157 * Sets the control to its pristine state.
35159 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
35160 * state (`ng-pristine` class). A model is considered to be pristine when the control
35161 * has not been changed from when first compiled.
35163 this.$setPristine = function() {
35164 ctrl.$dirty = false;
35165 ctrl.$pristine = true;
35166 $animate.removeClass($element, DIRTY_CLASS);
35167 $animate.addClass($element, PRISTINE_CLASS);
35172 * @name ngModel.NgModelController#$setDirty
35175 * Sets the control to its dirty state.
35177 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
35178 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
35179 * from when first compiled.
35181 this.$setDirty = function() {
35182 ctrl.$dirty = true;
35183 ctrl.$pristine = false;
35184 $animate.removeClass($element, PRISTINE_CLASS);
35185 $animate.addClass($element, DIRTY_CLASS);
35186 ctrl.$$parentForm.$setDirty();
35191 * @name ngModel.NgModelController#$setUntouched
35194 * Sets the control to its untouched state.
35196 * This method can be called to remove the `ng-touched` class and set the control to its
35197 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
35198 * by default, however this function can be used to restore that state if the model has
35199 * already been touched by the user.
35201 this.$setUntouched = function() {
35202 ctrl.$touched = false;
35203 ctrl.$untouched = true;
35204 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
35209 * @name ngModel.NgModelController#$setTouched
35212 * Sets the control to its touched state.
35214 * This method can be called to remove the `ng-untouched` class and set the control to its
35215 * touched state (`ng-touched` class). A model is considered to be touched when the user has
35216 * first focused the control element and then shifted focus away from the control (blur event).
35218 this.$setTouched = function() {
35219 ctrl.$touched = true;
35220 ctrl.$untouched = false;
35221 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
35226 * @name ngModel.NgModelController#$rollbackViewValue
35229 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
35230 * which may be caused by a pending debounced event or because the input is waiting for a some
35233 * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
35234 * depend on special events such as blur, you can have a situation where there is a period when
35235 * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
35237 * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
35238 * and reset the input to the last committed view value.
35240 * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
35241 * programmatically before these debounced/future events have resolved/occurred, because Angular's
35242 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
35244 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
35245 * input which may have such events pending. This is important in order to make sure that the
35246 * input field will be updated with the new model value and any pending operations are cancelled.
35248 * <example name="ng-model-cancel-update" module="cancel-update-example">
35249 * <file name="app.js">
35250 * angular.module('cancel-update-example', [])
35252 * .controller('CancelUpdateController', ['$scope', function($scope) {
35253 * $scope.model = {};
35255 * $scope.setEmpty = function(e, value, rollback) {
35256 * if (e.keyCode == 27) {
35257 * e.preventDefault();
35259 * $scope.myForm[value].$rollbackViewValue();
35261 * $scope.model[value] = '';
35266 * <file name="index.html">
35267 * <div ng-controller="CancelUpdateController">
35268 * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
35269 * empty them. Follow these steps and observe the difference:</p>
35271 * <li>Type something in the input. You will see that the model is not yet updated</li>
35272 * <li>Press the Escape key.
35274 * <li> In the first example, nothing happens, because the model is already '', and no
35275 * update is detected. If you blur the input, the model will be set to the current view.
35277 * <li> In the second example, the pending update is cancelled, and the input is set back
35278 * to the last committed view value (''). Blurring the input does nothing.
35284 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
35286 * <p id="inputDescription1">Without $rollbackViewValue():</p>
35287 * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
35288 * ng-keydown="setEmpty($event, 'value1')">
35289 * value1: "{{ model.value1 }}"
35293 * <p id="inputDescription2">With $rollbackViewValue():</p>
35294 * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
35295 * ng-keydown="setEmpty($event, 'value2', true)">
35296 * value2: "{{ model.value2 }}"
35301 <file name="style.css">
35303 display: table-cell;
35306 padding-right: 30px;
35312 this.$rollbackViewValue = function() {
35313 $timeout.cancel(pendingDebounce);
35314 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
35320 * @name ngModel.NgModelController#$validate
35323 * Runs each of the registered validators (first synchronous validators and then
35324 * asynchronous validators).
35325 * If the validity changes to invalid, the model will be set to `undefined`,
35326 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
35327 * If the validity changes to valid, it will set the model to the last available valid
35328 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
35330 this.$validate = function() {
35331 // ignore $validate before model is initialized
35332 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
35336 var viewValue = ctrl.$$lastCommittedViewValue;
35337 // Note: we use the $$rawModelValue as $modelValue might have been
35338 // set to undefined during a view -> model update that found validation
35339 // errors. We can't parse the view here, since that could change
35340 // the model although neither viewValue nor the model on the scope changed
35341 var modelValue = ctrl.$$rawModelValue;
35343 var prevValid = ctrl.$valid;
35344 var prevModelValue = ctrl.$modelValue;
35346 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
35348 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
35349 // If there was no change in validity, don't update the model
35350 // This prevents changing an invalid modelValue to undefined
35351 if (!allowInvalid && prevValid !== allValid) {
35352 // Note: Don't check ctrl.$valid here, as we could have
35353 // external validators (e.g. calculated on the server),
35354 // that just call $setValidity and need the model value
35355 // to calculate their validity.
35356 ctrl.$modelValue = allValid ? modelValue : undefined;
35358 if (ctrl.$modelValue !== prevModelValue) {
35359 ctrl.$$writeModelToScope();
35366 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
35367 currentValidationRunId++;
35368 var localValidationRunId = currentValidationRunId;
35370 // check parser error
35371 if (!processParseErrors()) {
35372 validationDone(false);
35375 if (!processSyncValidators()) {
35376 validationDone(false);
35379 processAsyncValidators();
35381 function processParseErrors() {
35382 var errorKey = ctrl.$$parserName || 'parse';
35383 if (isUndefined(parserValid)) {
35384 setValidity(errorKey, null);
35386 if (!parserValid) {
35387 forEach(ctrl.$validators, function(v, name) {
35388 setValidity(name, null);
35390 forEach(ctrl.$asyncValidators, function(v, name) {
35391 setValidity(name, null);
35394 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
35395 setValidity(errorKey, parserValid);
35396 return parserValid;
35401 function processSyncValidators() {
35402 var syncValidatorsValid = true;
35403 forEach(ctrl.$validators, function(validator, name) {
35404 var result = validator(modelValue, viewValue);
35405 syncValidatorsValid = syncValidatorsValid && result;
35406 setValidity(name, result);
35408 if (!syncValidatorsValid) {
35409 forEach(ctrl.$asyncValidators, function(v, name) {
35410 setValidity(name, null);
35417 function processAsyncValidators() {
35418 var validatorPromises = [];
35419 var allValid = true;
35420 forEach(ctrl.$asyncValidators, function(validator, name) {
35421 var promise = validator(modelValue, viewValue);
35422 if (!isPromiseLike(promise)) {
35423 throw ngModelMinErr('nopromise',
35424 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
35426 setValidity(name, undefined);
35427 validatorPromises.push(promise.then(function() {
35428 setValidity(name, true);
35429 }, function(error) {
35431 setValidity(name, false);
35434 if (!validatorPromises.length) {
35435 validationDone(true);
35437 $q.all(validatorPromises).then(function() {
35438 validationDone(allValid);
35443 function setValidity(name, isValid) {
35444 if (localValidationRunId === currentValidationRunId) {
35445 ctrl.$setValidity(name, isValid);
35449 function validationDone(allValid) {
35450 if (localValidationRunId === currentValidationRunId) {
35452 doneCallback(allValid);
35459 * @name ngModel.NgModelController#$commitViewValue
35462 * Commit a pending update to the `$modelValue`.
35464 * Updates may be pending by a debounced event or because the input is waiting for a some future
35465 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
35466 * usually handles calling this in response to input events.
35468 this.$commitViewValue = function() {
35469 var viewValue = ctrl.$viewValue;
35471 $timeout.cancel(pendingDebounce);
35473 // If the view value has not changed then we should just exit, except in the case where there is
35474 // a native validator on the element. In this case the validation state may have changed even though
35475 // the viewValue has stayed empty.
35476 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
35479 ctrl.$$updateEmptyClasses(viewValue);
35480 ctrl.$$lastCommittedViewValue = viewValue;
35483 if (ctrl.$pristine) {
35486 this.$$parseAndValidate();
35489 this.$$parseAndValidate = function() {
35490 var viewValue = ctrl.$$lastCommittedViewValue;
35491 var modelValue = viewValue;
35492 parserValid = isUndefined(modelValue) ? undefined : true;
35495 for (var i = 0; i < ctrl.$parsers.length; i++) {
35496 modelValue = ctrl.$parsers[i](modelValue);
35497 if (isUndefined(modelValue)) {
35498 parserValid = false;
35503 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
35504 // ctrl.$modelValue has not been touched yet...
35505 ctrl.$modelValue = ngModelGet($scope);
35507 var prevModelValue = ctrl.$modelValue;
35508 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
35509 ctrl.$$rawModelValue = modelValue;
35511 if (allowInvalid) {
35512 ctrl.$modelValue = modelValue;
35513 writeToModelIfNeeded();
35516 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
35517 // This can happen if e.g. $setViewValue is called from inside a parser
35518 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
35519 if (!allowInvalid) {
35520 // Note: Don't check ctrl.$valid here, as we could have
35521 // external validators (e.g. calculated on the server),
35522 // that just call $setValidity and need the model value
35523 // to calculate their validity.
35524 ctrl.$modelValue = allValid ? modelValue : undefined;
35525 writeToModelIfNeeded();
35529 function writeToModelIfNeeded() {
35530 if (ctrl.$modelValue !== prevModelValue) {
35531 ctrl.$$writeModelToScope();
35536 this.$$writeModelToScope = function() {
35537 ngModelSet($scope, ctrl.$modelValue);
35538 forEach(ctrl.$viewChangeListeners, function(listener) {
35542 $exceptionHandler(e);
35549 * @name ngModel.NgModelController#$setViewValue
35552 * Update the view value.
35554 * This method should be called when a control wants to change the view value; typically,
35555 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
35556 * directive calls it when the value of the input changes and {@link ng.directive:select select}
35557 * calls it when an option is selected.
35559 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
35560 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
35561 * value sent directly for processing, finally to be applied to `$modelValue` and then the
35562 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
35563 * in the `$viewChangeListeners` list, are called.
35565 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
35566 * and the `default` trigger is not listed, all those actions will remain pending until one of the
35567 * `updateOn` events is triggered on the DOM element.
35568 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
35569 * directive is used with a custom debounce for this particular event.
35570 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
35571 * is specified, once the timer runs out.
35573 * When used with standard inputs, the view value will always be a string (which is in some cases
35574 * parsed into another type, such as a `Date` object for `input[date]`.)
35575 * However, custom controls might also pass objects to this method. In this case, we should make
35576 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
35577 * perform a deep watch of objects, it only looks for a change of identity. If you only change
35578 * the property of the object then ngModel will not realize that the object has changed and
35579 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
35580 * not change properties of the copy once it has been passed to `$setViewValue`.
35581 * Otherwise you may cause the model value on the scope to change incorrectly.
35583 * <div class="alert alert-info">
35584 * In any case, the value passed to the method should always reflect the current value
35585 * of the control. For example, if you are calling `$setViewValue` for an input element,
35586 * you should pass the input DOM value. Otherwise, the control and the scope model become
35587 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
35588 * the control's DOM value in any way. If we want to change the control's DOM value
35589 * programmatically, we should update the `ngModel` scope expression. Its new value will be
35590 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
35591 * to update the DOM, and finally call `$validate` on it.
35594 * @param {*} value value from the view.
35595 * @param {string} trigger Event that triggered the update.
35597 this.$setViewValue = function(value, trigger) {
35598 ctrl.$viewValue = value;
35599 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
35600 ctrl.$$debounceViewValueCommit(trigger);
35604 this.$$debounceViewValueCommit = function(trigger) {
35605 var debounceDelay = 0,
35606 options = ctrl.$options,
35609 if (options && isDefined(options.debounce)) {
35610 debounce = options.debounce;
35611 if (isNumber(debounce)) {
35612 debounceDelay = debounce;
35613 } else if (isNumber(debounce[trigger])) {
35614 debounceDelay = debounce[trigger];
35615 } else if (isNumber(debounce['default'])) {
35616 debounceDelay = debounce['default'];
35620 $timeout.cancel(pendingDebounce);
35621 if (debounceDelay) {
35622 pendingDebounce = $timeout(function() {
35623 ctrl.$commitViewValue();
35625 } else if ($rootScope.$$phase) {
35626 ctrl.$commitViewValue();
35628 $scope.$apply(function() {
35629 ctrl.$commitViewValue();
35635 // Note: we cannot use a normal scope.$watch as we want to detect the following:
35636 // 1. scope value is 'a'
35637 // 2. user enters 'b'
35638 // 3. ng-change kicks in and reverts scope value to 'a'
35639 // -> scope value did not change since the last digest as
35640 // ng-change executes in apply phase
35641 // 4. view should be changed back to 'a'
35642 $scope.$watch(function ngModelWatch() {
35643 var modelValue = ngModelGet($scope);
35645 // if scope model value and ngModel value are out of sync
35646 // TODO(perf): why not move this to the action fn?
35647 if (modelValue !== ctrl.$modelValue &&
35648 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
35649 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
35651 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
35652 parserValid = undefined;
35654 var formatters = ctrl.$formatters,
35655 idx = formatters.length;
35657 var viewValue = modelValue;
35659 viewValue = formatters[idx](viewValue);
35661 if (ctrl.$viewValue !== viewValue) {
35662 ctrl.$$updateEmptyClasses(viewValue);
35663 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
35666 ctrl.$$runValidators(modelValue, viewValue, noop);
35683 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
35684 * property on the scope using {@link ngModel.NgModelController NgModelController},
35685 * which is created and exposed by this directive.
35687 * `ngModel` is responsible for:
35689 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
35691 * - Providing validation behavior (i.e. required, number, email, url).
35692 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
35693 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
35694 * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
35695 * - Registering the control with its parent {@link ng.directive:form form}.
35697 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
35698 * current scope. If the property doesn't already exist on this scope, it will be created
35699 * implicitly and added to the scope.
35701 * For best practices on using `ngModel`, see:
35703 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
35705 * For basic examples, how to use `ngModel`, see:
35707 * - {@link ng.directive:input input}
35708 * - {@link input[text] text}
35709 * - {@link input[checkbox] checkbox}
35710 * - {@link input[radio] radio}
35711 * - {@link input[number] number}
35712 * - {@link input[email] email}
35713 * - {@link input[url] url}
35714 * - {@link input[date] date}
35715 * - {@link input[datetime-local] datetime-local}
35716 * - {@link input[time] time}
35717 * - {@link input[month] month}
35718 * - {@link input[week] week}
35719 * - {@link ng.directive:select select}
35720 * - {@link ng.directive:textarea textarea}
35722 * # Complex Models (objects or collections)
35724 * By default, `ngModel` watches the model by reference, not value. This is important to know when
35725 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
35726 * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
35728 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
35730 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
35731 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
35732 * if the select is given the `multiple` attribute.
35734 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
35735 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
35736 * not trigger a re-rendering of the model.
35739 * The following CSS classes are added and removed on the associated input/select/textarea element
35740 * depending on the validity of the model.
35742 * - `ng-valid`: the model is valid
35743 * - `ng-invalid`: the model is invalid
35744 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
35745 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
35746 * - `ng-pristine`: the control hasn't been interacted with yet
35747 * - `ng-dirty`: the control has been interacted with
35748 * - `ng-touched`: the control has been blurred
35749 * - `ng-untouched`: the control hasn't been blurred
35750 * - `ng-pending`: any `$asyncValidators` are unfulfilled
35751 * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
35752 * by the {@link ngModel.NgModelController#$isEmpty} method
35753 * - `ng-not-empty`: the view contains a non-empty value
35755 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
35757 * ## Animation Hooks
35759 * Animations within models are triggered when any of the associated CSS classes are added and removed
35760 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
35761 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
35762 * The animations that are triggered within ngModel are similar to how they work in ngClass and
35763 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
35765 * The following example shows a simple way to utilize CSS transitions to style an input element
35766 * that has been rendered as invalid after it has been validated:
35769 * //be sure to include ngAnimate as a module to hook into more
35770 * //advanced animations
35772 * transition:0.5s linear all;
35773 * background: white;
35775 * .my-input.ng-invalid {
35782 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
35783 <file name="index.html">
35785 angular.module('inputExample', [])
35786 .controller('ExampleController', ['$scope', function($scope) {
35792 transition:all linear 0.5s;
35793 background: transparent;
35795 .my-input.ng-invalid {
35800 <p id="inputDescription">
35801 Update input to see transitions when valid/invalid.
35802 Integer is a valid value.
35804 <form name="testForm" ng-controller="ExampleController">
35805 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
35806 aria-describedby="inputDescription" />
35811 * ## Binding to a getter/setter
35813 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
35814 * function that returns a representation of the model when called with zero arguments, and sets
35815 * the internal state of a model when called with an argument. It's sometimes useful to use this
35816 * for models that have an internal representation that's different from what the model exposes
35819 * <div class="alert alert-success">
35820 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
35821 * frequently than other parts of your code.
35824 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
35825 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
35826 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
35827 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
35829 * The following example shows how to use `ngModel` with a getter/setter:
35832 * <example name="ngModel-getter-setter" module="getterSetterExample">
35833 <file name="index.html">
35834 <div ng-controller="ExampleController">
35835 <form name="userForm">
35837 <input type="text" name="userName"
35838 ng-model="user.name"
35839 ng-model-options="{ getterSetter: true }" />
35842 <pre>user.name = <span ng-bind="user.name()"></span></pre>
35845 <file name="app.js">
35846 angular.module('getterSetterExample', [])
35847 .controller('ExampleController', ['$scope', function($scope) {
35848 var _name = 'Brian';
35850 name: function(newName) {
35851 // Note that newName can be undefined for two reasons:
35852 // 1. Because it is called as a getter and thus called with no arguments
35853 // 2. Because the property should actually be set to undefined. This happens e.g. if the
35854 // input is invalid
35855 return arguments.length ? (_name = newName) : _name;
35862 var ngModelDirective = ['$rootScope', function($rootScope) {
35865 require: ['ngModel', '^?form', '^?ngModelOptions'],
35866 controller: NgModelController,
35867 // Prelink needs to run before any input directive
35868 // so that we can set the NgModelOptions in NgModelController
35869 // before anyone else uses it.
35871 compile: function ngModelCompile(element) {
35872 // Setup initial state of the control
35873 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
35876 pre: function ngModelPreLink(scope, element, attr, ctrls) {
35877 var modelCtrl = ctrls[0],
35878 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
35880 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
35882 // notify others, especially parent forms
35883 formCtrl.$addControl(modelCtrl);
35885 attr.$observe('name', function(newValue) {
35886 if (modelCtrl.$name !== newValue) {
35887 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
35891 scope.$on('$destroy', function() {
35892 modelCtrl.$$parentForm.$removeControl(modelCtrl);
35895 post: function ngModelPostLink(scope, element, attr, ctrls) {
35896 var modelCtrl = ctrls[0];
35897 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
35898 element.on(modelCtrl.$options.updateOn, function(ev) {
35899 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
35903 element.on('blur', function(ev) {
35904 if (modelCtrl.$touched) return;
35906 if ($rootScope.$$phase) {
35907 scope.$evalAsync(modelCtrl.$setTouched);
35909 scope.$apply(modelCtrl.$setTouched);
35918 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
35922 * @name ngModelOptions
35925 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
35926 * events that will trigger a model update and/or a debouncing delay so that the actual update only
35927 * takes place when a timer expires; this timer will be reset after another change takes place.
35929 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
35930 * be different from the value in the actual model. This means that if you update the model you
35931 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
35932 * order to make sure it is synchronized with the model and that any debounced action is canceled.
35934 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
35935 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
35936 * important because `form` controllers are published to the related scope under the name in their
35937 * `name` attribute.
35939 * Any pending changes will take place immediately when an enclosing form is submitted via the
35940 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
35941 * to have access to the updated model.
35943 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
35945 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
35946 * - `updateOn`: string specifying which event should the input be bound to. You can set several
35947 * events using an space delimited list. There is a special event called `default` that
35948 * matches the default events belonging of the control.
35949 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
35950 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
35951 * custom value for each event. For example:
35952 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
35953 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
35954 * not validate correctly instead of the default behavior of setting the model to undefined.
35955 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
35956 `ngModel` as getters/setters.
35957 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
35958 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
35959 * continental US time zone abbreviations, but for general use, use a time zone offset, for
35960 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
35961 * If not specified, the timezone of the browser will be used.
35965 The following example shows how to override immediate updates. Changes on the inputs within the
35966 form will update the model only when the control loses focus (blur event). If `escape` key is
35967 pressed while the input field is focused, the value is reset to the value in the current model.
35969 <example name="ngModelOptions-directive-blur" module="optionsExample">
35970 <file name="index.html">
35971 <div ng-controller="ExampleController">
35972 <form name="userForm">
35974 <input type="text" name="userName"
35975 ng-model="user.name"
35976 ng-model-options="{ updateOn: 'blur' }"
35977 ng-keyup="cancel($event)" />
35980 <input type="text" ng-model="user.data" />
35983 <pre>user.name = <span ng-bind="user.name"></span></pre>
35984 <pre>user.data = <span ng-bind="user.data"></span></pre>
35987 <file name="app.js">
35988 angular.module('optionsExample', [])
35989 .controller('ExampleController', ['$scope', function($scope) {
35990 $scope.user = { name: 'John', data: '' };
35992 $scope.cancel = function(e) {
35993 if (e.keyCode == 27) {
35994 $scope.userForm.userName.$rollbackViewValue();
35999 <file name="protractor.js" type="protractor">
36000 var model = element(by.binding('user.name'));
36001 var input = element(by.model('user.name'));
36002 var other = element(by.model('user.data'));
36004 it('should allow custom events', function() {
36005 input.sendKeys(' Doe');
36007 expect(model.getText()).toEqual('John');
36009 expect(model.getText()).toEqual('John Doe');
36012 it('should $rollbackViewValue when model changes', function() {
36013 input.sendKeys(' Doe');
36014 expect(input.getAttribute('value')).toEqual('John Doe');
36015 input.sendKeys(protractor.Key.ESCAPE);
36016 expect(input.getAttribute('value')).toEqual('John');
36018 expect(model.getText()).toEqual('John');
36023 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
36024 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
36026 <example name="ngModelOptions-directive-debounce" module="optionsExample">
36027 <file name="index.html">
36028 <div ng-controller="ExampleController">
36029 <form name="userForm">
36031 <input type="text" name="userName"
36032 ng-model="user.name"
36033 ng-model-options="{ debounce: 1000 }" />
36035 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
36038 <pre>user.name = <span ng-bind="user.name"></span></pre>
36041 <file name="app.js">
36042 angular.module('optionsExample', [])
36043 .controller('ExampleController', ['$scope', function($scope) {
36044 $scope.user = { name: 'Igor' };
36049 This one shows how to bind to getter/setters:
36051 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
36052 <file name="index.html">
36053 <div ng-controller="ExampleController">
36054 <form name="userForm">
36056 <input type="text" name="userName"
36057 ng-model="user.name"
36058 ng-model-options="{ getterSetter: true }" />
36061 <pre>user.name = <span ng-bind="user.name()"></span></pre>
36064 <file name="app.js">
36065 angular.module('getterSetterExample', [])
36066 .controller('ExampleController', ['$scope', function($scope) {
36067 var _name = 'Brian';
36069 name: function(newName) {
36070 // Note that newName can be undefined for two reasons:
36071 // 1. Because it is called as a getter and thus called with no arguments
36072 // 2. Because the property should actually be set to undefined. This happens e.g. if the
36073 // input is invalid
36074 return arguments.length ? (_name = newName) : _name;
36081 var ngModelOptionsDirective = function() {
36084 controller: ['$scope', '$attrs', function($scope, $attrs) {
36086 this.$options = copy($scope.$eval($attrs.ngModelOptions));
36087 // Allow adding/overriding bound events
36088 if (isDefined(this.$options.updateOn)) {
36089 this.$options.updateOnDefault = false;
36090 // extract "default" pseudo-event from list of events that can trigger a model update
36091 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
36092 that.$options.updateOnDefault = true;
36096 this.$options.updateOnDefault = true;
36105 function addSetValidityMethod(context) {
36106 var ctrl = context.ctrl,
36107 $element = context.$element,
36110 unset = context.unset,
36111 $animate = context.$animate;
36113 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
36115 ctrl.$setValidity = setValidity;
36117 function setValidity(validationErrorKey, state, controller) {
36118 if (isUndefined(state)) {
36119 createAndSet('$pending', validationErrorKey, controller);
36121 unsetAndCleanup('$pending', validationErrorKey, controller);
36123 if (!isBoolean(state)) {
36124 unset(ctrl.$error, validationErrorKey, controller);
36125 unset(ctrl.$$success, validationErrorKey, controller);
36128 unset(ctrl.$error, validationErrorKey, controller);
36129 set(ctrl.$$success, validationErrorKey, controller);
36131 set(ctrl.$error, validationErrorKey, controller);
36132 unset(ctrl.$$success, validationErrorKey, controller);
36135 if (ctrl.$pending) {
36136 cachedToggleClass(PENDING_CLASS, true);
36137 ctrl.$valid = ctrl.$invalid = undefined;
36138 toggleValidationCss('', null);
36140 cachedToggleClass(PENDING_CLASS, false);
36141 ctrl.$valid = isObjectEmpty(ctrl.$error);
36142 ctrl.$invalid = !ctrl.$valid;
36143 toggleValidationCss('', ctrl.$valid);
36146 // re-read the state as the set/unset methods could have
36147 // combined state in ctrl.$error[validationError] (used for forms),
36148 // where setting/unsetting only increments/decrements the value,
36149 // and does not replace it.
36151 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
36152 combinedState = undefined;
36153 } else if (ctrl.$error[validationErrorKey]) {
36154 combinedState = false;
36155 } else if (ctrl.$$success[validationErrorKey]) {
36156 combinedState = true;
36158 combinedState = null;
36161 toggleValidationCss(validationErrorKey, combinedState);
36162 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
36165 function createAndSet(name, value, controller) {
36169 set(ctrl[name], value, controller);
36172 function unsetAndCleanup(name, value, controller) {
36174 unset(ctrl[name], value, controller);
36176 if (isObjectEmpty(ctrl[name])) {
36177 ctrl[name] = undefined;
36181 function cachedToggleClass(className, switchValue) {
36182 if (switchValue && !classCache[className]) {
36183 $animate.addClass($element, className);
36184 classCache[className] = true;
36185 } else if (!switchValue && classCache[className]) {
36186 $animate.removeClass($element, className);
36187 classCache[className] = false;
36191 function toggleValidationCss(validationErrorKey, isValid) {
36192 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
36194 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
36195 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
36199 function isObjectEmpty(obj) {
36201 for (var prop in obj) {
36202 if (obj.hasOwnProperty(prop)) {
36212 * @name ngNonBindable
36217 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
36218 * DOM element. This is useful if the element contains what appears to be Angular directives and
36219 * bindings but which should be ignored by Angular. This could be the case if you have a site that
36220 * displays snippets of code, for instance.
36225 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
36226 * but the one wrapped in `ngNonBindable` is left alone.
36230 <file name="index.html">
36231 <div>Normal: {{1 + 2}}</div>
36232 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
36234 <file name="protractor.js" type="protractor">
36235 it('should check ng-non-bindable', function() {
36236 expect(element(by.binding('1 + 2')).getText()).toContain('3');
36237 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
36242 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
36244 /* global jqLiteRemove */
36246 var ngOptionsMinErr = minErr('ngOptions');
36255 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
36256 * elements for the `<select>` element using the array or object obtained by evaluating the
36257 * `ngOptions` comprehension expression.
36259 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
36260 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
36261 * increasing speed by not creating a new scope for each repeated instance, as well as providing
36262 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
36263 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
36264 * to a non-string value. This is because an option element can only be bound to string values at
36267 * When an item in the `<select>` menu is selected, the array element or object property
36268 * represented by the selected option will be bound to the model identified by the `ngModel`
36271 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
36272 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
36273 * option. See example below for demonstration.
36275 * ## Complex Models (objects or collections)
36277 * By default, `ngModel` watches the model by reference, not value. This is important to know when
36278 * binding the select to a model that is an object or a collection.
36280 * One issue occurs if you want to preselect an option. For example, if you set
36281 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
36282 * because the objects are not identical. So by default, you should always reference the item in your collection
36283 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
36285 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
36286 * of the item not by reference, but by the result of the `track by` expression. For example, if your
36287 * collection items have an id property, you would `track by item.id`.
36289 * A different issue with objects or collections is that ngModel won't detect if an object property or
36290 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
36291 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
36292 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
36293 * has not changed identity, but only a property on the object or an item in the collection changes.
36295 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
36296 * if the model is an array). This means that changing a property deeper than the first level inside the
36297 * object/collection will not trigger a re-rendering.
36299 * ## `select` **`as`**
36301 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
36302 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
36303 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
36304 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
36307 * ### `select` **`as`** and **`track by`**
36309 * <div class="alert alert-warning">
36310 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
36313 * Given this array of items on the $scope:
36316 * $scope.items = [{
36319 * subItem: { name: 'aSubItem' }
36323 * subItem: { name: 'bSubItem' }
36330 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
36333 * $scope.selected = $scope.items[0];
36336 * but this will not work:
36339 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
36342 * $scope.selected = $scope.items[0].subItem;
36345 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
36346 * `items` array. Because the selected option has been set programmatically in the controller, the
36347 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
36348 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
36349 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
36350 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
36351 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
36354 * @param {string} ngModel Assignable angular expression to data-bind to.
36355 * @param {string=} name Property name of the form under which the control is published.
36356 * @param {string=} required The control is considered valid only if value is entered.
36357 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
36358 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
36359 * `required` when you want to data-bind to the `required` attribute.
36360 * @param {comprehension_expression=} ngOptions in one of the following forms:
36362 * * for array data sources:
36363 * * `label` **`for`** `value` **`in`** `array`
36364 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
36365 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
36366 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
36367 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
36368 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
36369 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
36370 * (for including a filter with `track by`)
36371 * * for object data sources:
36372 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
36373 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
36374 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
36375 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
36376 * * `select` **`as`** `label` **`group by`** `group`
36377 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
36378 * * `select` **`as`** `label` **`disable when`** `disable`
36379 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
36383 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
36384 * * `value`: local variable which will refer to each item in the `array` or each property value
36385 * of `object` during iteration.
36386 * * `key`: local variable which will refer to a property name in `object` during iteration.
36387 * * `label`: The result of this expression will be the label for `<option>` element. The
36388 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
36389 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
36390 * element. If not specified, `select` expression will default to `value`.
36391 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
36393 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
36394 * element. Return `true` to disable.
36395 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
36396 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
36397 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
36398 * even when the options are recreated (e.g. reloaded from the server).
36401 <example module="selectExample">
36402 <file name="index.html">
36404 angular.module('selectExample', [])
36405 .controller('ExampleController', ['$scope', function($scope) {
36407 {name:'black', shade:'dark'},
36408 {name:'white', shade:'light', notAnOption: true},
36409 {name:'red', shade:'dark'},
36410 {name:'blue', shade:'dark', notAnOption: true},
36411 {name:'yellow', shade:'light', notAnOption: false}
36413 $scope.myColor = $scope.colors[2]; // red
36416 <div ng-controller="ExampleController">
36418 <li ng-repeat="color in colors">
36419 <label>Name: <input ng-model="color.name"></label>
36420 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
36421 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
36424 <button ng-click="colors.push({})">add</button>
36428 <label>Color (null not allowed):
36429 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
36431 <label>Color (null allowed):
36432 <span class="nullable">
36433 <select ng-model="myColor" ng-options="color.name for color in colors">
36434 <option value="">-- choose color --</option>
36436 </span></label><br/>
36438 <label>Color grouped by shade:
36439 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
36443 <label>Color grouped by shade, with some disabled:
36444 <select ng-model="myColor"
36445 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
36451 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
36454 Currently selected: {{ {selected_color:myColor} }}
36455 <div style="border:solid 1px black; height:20px"
36456 ng-style="{'background-color':myColor.name}">
36460 <file name="protractor.js" type="protractor">
36461 it('should check ng-options', function() {
36462 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
36463 element.all(by.model('myColor')).first().click();
36464 element.all(by.css('select[ng-model="myColor"] option')).first().click();
36465 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
36466 element(by.css('.nullable select[ng-model="myColor"]')).click();
36467 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
36468 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
36474 // jshint maxlen: false
36475 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
36476 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]+?))?$/;
36477 // 1: value expression (valueFn)
36478 // 2: label expression (displayFn)
36479 // 3: group by expression (groupByFn)
36480 // 4: disable when expression (disableWhenFn)
36481 // 5: array item variable name
36482 // 6: object item key variable name
36483 // 7: object item value variable name
36484 // 8: collection expression
36485 // 9: track by expression
36486 // jshint maxlen: 100
36489 var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36491 function parseOptionsExpression(optionsExp, selectElement, scope) {
36493 var match = optionsExp.match(NG_OPTIONS_REGEXP);
36495 throw ngOptionsMinErr('iexp',
36496 "Expected expression in form of " +
36497 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
36498 " but got '{0}'. Element: {1}",
36499 optionsExp, startingTag(selectElement));
36502 // Extract the parts from the ngOptions expression
36504 // The variable name for the value of the item in the collection
36505 var valueName = match[5] || match[7];
36506 // The variable name for the key of the item in the collection
36507 var keyName = match[6];
36509 // An expression that generates the viewValue for an option if there is a label expression
36510 var selectAs = / as /.test(match[0]) && match[1];
36511 // An expression that is used to track the id of each object in the options collection
36512 var trackBy = match[9];
36513 // An expression that generates the viewValue for an option if there is no label expression
36514 var valueFn = $parse(match[2] ? match[1] : valueName);
36515 var selectAsFn = selectAs && $parse(selectAs);
36516 var viewValueFn = selectAsFn || valueFn;
36517 var trackByFn = trackBy && $parse(trackBy);
36519 // Get the value by which we are going to track the option
36520 // if we have a trackFn then use that (passing scope and locals)
36521 // otherwise just hash the given viewValue
36522 var getTrackByValueFn = trackBy ?
36523 function(value, locals) { return trackByFn(scope, locals); } :
36524 function getHashOfValue(value) { return hashKey(value); };
36525 var getTrackByValue = function(value, key) {
36526 return getTrackByValueFn(value, getLocals(value, key));
36529 var displayFn = $parse(match[2] || match[1]);
36530 var groupByFn = $parse(match[3] || '');
36531 var disableWhenFn = $parse(match[4] || '');
36532 var valuesFn = $parse(match[8]);
36535 var getLocals = keyName ? function(value, key) {
36536 locals[keyName] = key;
36537 locals[valueName] = value;
36539 } : function(value) {
36540 locals[valueName] = value;
36545 function Option(selectValue, viewValue, label, group, disabled) {
36546 this.selectValue = selectValue;
36547 this.viewValue = viewValue;
36548 this.label = label;
36549 this.group = group;
36550 this.disabled = disabled;
36553 function getOptionValuesKeys(optionValues) {
36554 var optionValuesKeys;
36556 if (!keyName && isArrayLike(optionValues)) {
36557 optionValuesKeys = optionValues;
36559 // if object, extract keys, in enumeration order, unsorted
36560 optionValuesKeys = [];
36561 for (var itemKey in optionValues) {
36562 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
36563 optionValuesKeys.push(itemKey);
36567 return optionValuesKeys;
36572 getTrackByValue: getTrackByValue,
36573 getWatchables: $parse(valuesFn, function(optionValues) {
36574 // Create a collection of things that we would like to watch (watchedArray)
36575 // so that they can all be watched using a single $watchCollection
36576 // that only runs the handler once if anything changes
36577 var watchedArray = [];
36578 optionValues = optionValues || [];
36580 var optionValuesKeys = getOptionValuesKeys(optionValues);
36581 var optionValuesLength = optionValuesKeys.length;
36582 for (var index = 0; index < optionValuesLength; index++) {
36583 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
36584 var value = optionValues[key];
36586 var locals = getLocals(optionValues[key], key);
36587 var selectValue = getTrackByValueFn(optionValues[key], locals);
36588 watchedArray.push(selectValue);
36590 // Only need to watch the displayFn if there is a specific label expression
36591 if (match[2] || match[1]) {
36592 var label = displayFn(scope, locals);
36593 watchedArray.push(label);
36596 // Only need to watch the disableWhenFn if there is a specific disable expression
36598 var disableWhen = disableWhenFn(scope, locals);
36599 watchedArray.push(disableWhen);
36602 return watchedArray;
36605 getOptions: function() {
36607 var optionItems = [];
36608 var selectValueMap = {};
36610 // The option values were already computed in the `getWatchables` fn,
36611 // which must have been called to trigger `getOptions`
36612 var optionValues = valuesFn(scope) || [];
36613 var optionValuesKeys = getOptionValuesKeys(optionValues);
36614 var optionValuesLength = optionValuesKeys.length;
36616 for (var index = 0; index < optionValuesLength; index++) {
36617 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
36618 var value = optionValues[key];
36619 var locals = getLocals(value, key);
36620 var viewValue = viewValueFn(scope, locals);
36621 var selectValue = getTrackByValueFn(viewValue, locals);
36622 var label = displayFn(scope, locals);
36623 var group = groupByFn(scope, locals);
36624 var disabled = disableWhenFn(scope, locals);
36625 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
36627 optionItems.push(optionItem);
36628 selectValueMap[selectValue] = optionItem;
36632 items: optionItems,
36633 selectValueMap: selectValueMap,
36634 getOptionFromViewValue: function(value) {
36635 return selectValueMap[getTrackByValue(value)];
36637 getViewValueFromOption: function(option) {
36638 // If the viewValue could be an object that may be mutated by the application,
36639 // we need to make a copy and not return the reference to the value on the option.
36640 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
36648 // we can't just jqLite('<option>') since jqLite is not smart enough
36649 // to create it in <select> and IE barfs otherwise.
36650 var optionTemplate = document.createElement('option'),
36651 optGroupTemplate = document.createElement('optgroup');
36653 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
36655 var selectCtrl = ctrls[0];
36656 var ngModelCtrl = ctrls[1];
36657 var multiple = attr.multiple;
36659 // The emptyOption allows the application developer to provide their own custom "empty"
36660 // option when the viewValue does not match any of the option values.
36662 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
36663 if (children[i].value === '') {
36664 emptyOption = children.eq(i);
36669 var providedEmptyOption = !!emptyOption;
36671 var unknownOption = jqLite(optionTemplate.cloneNode(false));
36672 unknownOption.val('?');
36675 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
36678 var renderEmptyOption = function() {
36679 if (!providedEmptyOption) {
36680 selectElement.prepend(emptyOption);
36682 selectElement.val('');
36683 emptyOption.prop('selected', true); // needed for IE
36684 emptyOption.attr('selected', true);
36687 var removeEmptyOption = function() {
36688 if (!providedEmptyOption) {
36689 emptyOption.remove();
36694 var renderUnknownOption = function() {
36695 selectElement.prepend(unknownOption);
36696 selectElement.val('?');
36697 unknownOption.prop('selected', true); // needed for IE
36698 unknownOption.attr('selected', true);
36701 var removeUnknownOption = function() {
36702 unknownOption.remove();
36705 // Update the controller methods for multiple selectable options
36708 selectCtrl.writeValue = function writeNgOptionsValue(value) {
36709 var option = options.getOptionFromViewValue(value);
36711 if (option && !option.disabled) {
36712 if (selectElement[0].value !== option.selectValue) {
36713 removeUnknownOption();
36714 removeEmptyOption();
36716 selectElement[0].value = option.selectValue;
36717 option.element.selected = true;
36718 option.element.setAttribute('selected', 'selected');
36721 if (value === null || providedEmptyOption) {
36722 removeUnknownOption();
36723 renderEmptyOption();
36725 removeEmptyOption();
36726 renderUnknownOption();
36731 selectCtrl.readValue = function readNgOptionsValue() {
36733 var selectedOption = options.selectValueMap[selectElement.val()];
36735 if (selectedOption && !selectedOption.disabled) {
36736 removeEmptyOption();
36737 removeUnknownOption();
36738 return options.getViewValueFromOption(selectedOption);
36743 // If we are using `track by` then we must watch the tracked value on the model
36744 // since ngModel only watches for object identity change
36745 if (ngOptions.trackBy) {
36747 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
36748 function() { ngModelCtrl.$render(); }
36754 ngModelCtrl.$isEmpty = function(value) {
36755 return !value || value.length === 0;
36759 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
36760 options.items.forEach(function(option) {
36761 option.element.selected = false;
36765 value.forEach(function(item) {
36766 var option = options.getOptionFromViewValue(item);
36767 if (option && !option.disabled) option.element.selected = true;
36773 selectCtrl.readValue = function readNgOptionsMultiple() {
36774 var selectedValues = selectElement.val() || [],
36777 forEach(selectedValues, function(value) {
36778 var option = options.selectValueMap[value];
36779 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
36785 // If we are using `track by` then we must watch these tracked values on the model
36786 // since ngModel only watches for object identity change
36787 if (ngOptions.trackBy) {
36789 scope.$watchCollection(function() {
36790 if (isArray(ngModelCtrl.$viewValue)) {
36791 return ngModelCtrl.$viewValue.map(function(value) {
36792 return ngOptions.getTrackByValue(value);
36796 ngModelCtrl.$render();
36803 if (providedEmptyOption) {
36805 // we need to remove it before calling selectElement.empty() because otherwise IE will
36806 // remove the label from the element. wtf?
36807 emptyOption.remove();
36809 // compile the element since there might be bindings in it
36810 $compile(emptyOption)(scope);
36812 // remove the class, which is added automatically because we recompile the element and it
36813 // becomes the compilation root
36814 emptyOption.removeClass('ng-scope');
36816 emptyOption = jqLite(optionTemplate.cloneNode(false));
36819 // We need to do this here to ensure that the options object is defined
36820 // when we first hit it in writeNgOptionsValue
36823 // We will re-render the option elements if the option values or labels change
36824 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
36826 // ------------------------------------------------------------------ //
36829 function updateOptionElement(option, element) {
36830 option.element = element;
36831 element.disabled = option.disabled;
36832 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
36833 // selects in certain circumstances when multiple selects are next to each other and display
36834 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
36835 // See https://github.com/angular/angular.js/issues/11314 for more info.
36836 // This is unfortunately untestable with unit / e2e tests
36837 if (option.label !== element.label) {
36838 element.label = option.label;
36839 element.textContent = option.label;
36841 if (option.value !== element.value) element.value = option.selectValue;
36844 function addOrReuseElement(parent, current, type, templateElement) {
36846 // Check whether we can reuse the next element
36847 if (current && lowercase(current.nodeName) === type) {
36848 // The next element is the right type so reuse it
36851 // The next element is not the right type so create a new one
36852 element = templateElement.cloneNode(false);
36854 // There are no more elements so just append it to the select
36855 parent.appendChild(element);
36857 // The next element is not a group so insert the new one
36858 parent.insertBefore(element, current);
36865 function removeExcessElements(current) {
36868 next = current.nextSibling;
36869 jqLiteRemove(current);
36875 function skipEmptyAndUnknownOptions(current) {
36876 var emptyOption_ = emptyOption && emptyOption[0];
36877 var unknownOption_ = unknownOption && unknownOption[0];
36879 // We cannot rely on the extracted empty option being the same as the compiled empty option,
36880 // because the compiled empty option might have been replaced by a comment because
36881 // it had an "element" transclusion directive on it (such as ngIf)
36882 if (emptyOption_ || unknownOption_) {
36884 (current === emptyOption_ ||
36885 current === unknownOption_ ||
36886 current.nodeType === NODE_TYPE_COMMENT ||
36887 (nodeName_(current) === 'option' && current.value === ''))) {
36888 current = current.nextSibling;
36895 function updateOptions() {
36897 var previousValue = options && selectCtrl.readValue();
36899 options = ngOptions.getOptions();
36902 var currentElement = selectElement[0].firstChild;
36904 // Ensure that the empty option is always there if it was explicitly provided
36905 if (providedEmptyOption) {
36906 selectElement.prepend(emptyOption);
36909 currentElement = skipEmptyAndUnknownOptions(currentElement);
36911 options.items.forEach(function updateOption(option) {
36916 if (isDefined(option.group)) {
36918 // This option is to live in a group
36919 // See if we have already created this group
36920 group = groupMap[option.group];
36924 // We have not already created this group
36925 groupElement = addOrReuseElement(selectElement[0],
36929 // Move to the next element
36930 currentElement = groupElement.nextSibling;
36932 // Update the label on the group element
36933 groupElement.label = option.group;
36935 // Store it for use later
36936 group = groupMap[option.group] = {
36937 groupElement: groupElement,
36938 currentOptionElement: groupElement.firstChild
36943 // So now we have a group for this option we add the option to the group
36944 optionElement = addOrReuseElement(group.groupElement,
36945 group.currentOptionElement,
36948 updateOptionElement(option, optionElement);
36949 // Move to the next element
36950 group.currentOptionElement = optionElement.nextSibling;
36954 // This option is not in a group
36955 optionElement = addOrReuseElement(selectElement[0],
36959 updateOptionElement(option, optionElement);
36960 // Move to the next element
36961 currentElement = optionElement.nextSibling;
36966 // Now remove all excess options and group
36967 Object.keys(groupMap).forEach(function(key) {
36968 removeExcessElements(groupMap[key].currentOptionElement);
36970 removeExcessElements(currentElement);
36972 ngModelCtrl.$render();
36974 // Check to see if the value has changed due to the update to the options
36975 if (!ngModelCtrl.$isEmpty(previousValue)) {
36976 var nextValue = selectCtrl.readValue();
36977 var isNotPrimitive = ngOptions.trackBy || multiple;
36978 if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
36979 ngModelCtrl.$setViewValue(nextValue);
36980 ngModelCtrl.$render();
36990 require: ['select', 'ngModel'],
36992 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
36993 // Deactivate the SelectController.register method to prevent
36994 // option directives from accidentally registering themselves
36995 // (and unwanted $destroy handlers etc.)
36996 ctrls[0].registerOption = noop;
36998 post: ngOptionsPostLink
37005 * @name ngPluralize
37009 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
37010 * These rules are bundled with angular.js, but can be overridden
37011 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
37012 * by specifying the mappings between
37013 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
37014 * and the strings to be displayed.
37016 * # Plural categories and explicit number rules
37018 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
37019 * in Angular's default en-US locale: "one" and "other".
37021 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
37022 * any number that is not 1), an explicit number rule can only match one number. For example, the
37023 * explicit number rule for "3" matches the number 3. There are examples of plural categories
37024 * and explicit number rules throughout the rest of this documentation.
37026 * # Configuring ngPluralize
37027 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
37028 * You can also provide an optional attribute, `offset`.
37030 * The value of the `count` attribute can be either a string or an {@link guide/expression
37031 * Angular expression}; these are evaluated on the current scope for its bound value.
37033 * The `when` attribute specifies the mappings between plural categories and the actual
37034 * string to be displayed. The value of the attribute should be a JSON object.
37036 * The following example shows how to configure ngPluralize:
37039 * <ng-pluralize count="personCount"
37040 when="{'0': 'Nobody is viewing.',
37041 * 'one': '1 person is viewing.',
37042 * 'other': '{} people are viewing.'}">
37046 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
37047 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
37048 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
37049 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
37050 * show "a dozen people are viewing".
37052 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
37053 * into pluralized strings. In the previous example, Angular will replace `{}` with
37054 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
37055 * for <span ng-non-bindable>{{numberExpression}}</span>.
37057 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
37058 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
37060 * # Configuring ngPluralize with offset
37061 * The `offset` attribute allows further customization of pluralized text, which can result in
37062 * a better user experience. For example, instead of the message "4 people are viewing this document",
37063 * you might display "John, Kate and 2 others are viewing this document".
37064 * The offset attribute allows you to offset a number by any desired value.
37065 * Let's take a look at an example:
37068 * <ng-pluralize count="personCount" offset=2
37069 * when="{'0': 'Nobody is viewing.',
37070 * '1': '{{person1}} is viewing.',
37071 * '2': '{{person1}} and {{person2}} are viewing.',
37072 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
37073 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
37077 * Notice that we are still using two plural categories(one, other), but we added
37078 * three explicit number rules 0, 1 and 2.
37079 * When one person, perhaps John, views the document, "John is viewing" will be shown.
37080 * When three people view the document, no explicit number rule is found, so
37081 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
37082 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
37085 * Note that when you specify offsets, you must provide explicit number rules for
37086 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
37087 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
37088 * plural categories "one" and "other".
37090 * @param {string|expression} count The variable to be bound to.
37091 * @param {string} when The mapping between plural category to its corresponding strings.
37092 * @param {number=} offset Offset to deduct from the total number.
37095 <example module="pluralizeExample">
37096 <file name="index.html">
37098 angular.module('pluralizeExample', [])
37099 .controller('ExampleController', ['$scope', function($scope) {
37100 $scope.person1 = 'Igor';
37101 $scope.person2 = 'Misko';
37102 $scope.personCount = 1;
37105 <div ng-controller="ExampleController">
37106 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
37107 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
37108 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
37110 <!--- Example with simple pluralization rules for en locale --->
37112 <ng-pluralize count="personCount"
37113 when="{'0': 'Nobody is viewing.',
37114 'one': '1 person is viewing.',
37115 'other': '{} people are viewing.'}">
37116 </ng-pluralize><br>
37118 <!--- Example with offset --->
37120 <ng-pluralize count="personCount" offset=2
37121 when="{'0': 'Nobody is viewing.',
37122 '1': '{{person1}} is viewing.',
37123 '2': '{{person1}} and {{person2}} are viewing.',
37124 'one': '{{person1}}, {{person2}} and one other person are viewing.',
37125 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
37129 <file name="protractor.js" type="protractor">
37130 it('should show correct pluralized string', function() {
37131 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
37132 var withOffset = element.all(by.css('ng-pluralize')).get(1);
37133 var countInput = element(by.model('personCount'));
37135 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
37136 expect(withOffset.getText()).toEqual('Igor is viewing.');
37138 countInput.clear();
37139 countInput.sendKeys('0');
37141 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
37142 expect(withOffset.getText()).toEqual('Nobody is viewing.');
37144 countInput.clear();
37145 countInput.sendKeys('2');
37147 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
37148 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
37150 countInput.clear();
37151 countInput.sendKeys('3');
37153 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
37154 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
37156 countInput.clear();
37157 countInput.sendKeys('4');
37159 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
37160 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
37162 it('should show data-bound names', function() {
37163 var withOffset = element.all(by.css('ng-pluralize')).get(1);
37164 var personCount = element(by.model('personCount'));
37165 var person1 = element(by.model('person1'));
37166 var person2 = element(by.model('person2'));
37167 personCount.clear();
37168 personCount.sendKeys('4');
37170 person1.sendKeys('Di');
37172 person2.sendKeys('Vojta');
37173 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
37178 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
37180 IS_WHEN = /^when(Minus)?(.+)$/;
37183 link: function(scope, element, attr) {
37184 var numberExp = attr.count,
37185 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
37186 offset = attr.offset || 0,
37187 whens = scope.$eval(whenExp) || {},
37189 startSymbol = $interpolate.startSymbol(),
37190 endSymbol = $interpolate.endSymbol(),
37191 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
37192 watchRemover = angular.noop,
37195 forEach(attr, function(expression, attributeName) {
37196 var tmpMatch = IS_WHEN.exec(attributeName);
37198 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
37199 whens[whenKey] = element.attr(attr.$attr[attributeName]);
37202 forEach(whens, function(expression, key) {
37203 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
37207 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
37208 var count = parseFloat(newVal);
37209 var countIsNaN = isNaN(count);
37211 if (!countIsNaN && !(count in whens)) {
37212 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
37213 // Otherwise, check it against pluralization rules in $locale service.
37214 count = $locale.pluralCat(count - offset);
37217 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
37218 // In JS `NaN !== NaN`, so we have to explicitly check.
37219 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
37221 var whenExpFn = whensExpFns[count];
37222 if (isUndefined(whenExpFn)) {
37223 if (newVal != null) {
37224 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
37226 watchRemover = noop;
37227 updateElementText();
37229 watchRemover = scope.$watch(whenExpFn, updateElementText);
37235 function updateElementText(newText) {
37236 element.text(newText || '');
37248 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
37249 * instance gets its own scope, where the given loop variable is set to the current collection item,
37250 * and `$index` is set to the item index or key.
37252 * Special properties are exposed on the local scope of each template instance, including:
37254 * | Variable | Type | Details |
37255 * |-----------|-----------------|-----------------------------------------------------------------------------|
37256 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
37257 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
37258 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
37259 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
37260 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
37261 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
37263 * <div class="alert alert-info">
37264 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
37265 * This may be useful when, for instance, nesting ngRepeats.
37269 * # Iterating over object properties
37271 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
37275 * <div ng-repeat="(key, value) in myObj"> ... </div>
37278 * You need to be aware that the JavaScript specification does not define the order of keys
37279 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
37280 * used to sort the keys alphabetically.)
37282 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
37283 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
37284 * keys in the order in which they were defined, although there are exceptions when keys are deleted
37285 * 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).
37287 * If this is not desired, the recommended workaround is to convert your object into an array
37288 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
37289 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
37290 * or implement a `$watch` on the object yourself.
37293 * # Tracking and Duplicates
37295 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
37296 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
37298 * * When an item is added, a new instance of the template is added to the DOM.
37299 * * When an item is removed, its template instance is removed from the DOM.
37300 * * When items are reordered, their respective templates are reordered in the DOM.
37302 * To minimize creation of DOM elements, `ngRepeat` uses a function
37303 * to "keep track" of all items in the collection and their corresponding DOM elements.
37304 * For example, if an item is added to the collection, ngRepeat will know that all other items
37305 * already have DOM elements, and will not re-render them.
37307 * The default tracking function (which tracks items by their identity) does not allow
37308 * duplicate items in arrays. This is because when there are duplicates, it is not possible
37309 * to maintain a one-to-one mapping between collection items and DOM elements.
37311 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
37312 * with your own using the `track by` expression.
37314 * For example, you may track items by the index of each item in the collection, using the
37315 * special scope property `$index`:
37317 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
37322 * You may also use arbitrary expressions in `track by`, including references to custom functions
37325 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
37330 * <div class="alert alert-success">
37331 * If you are working with objects that have an identifier property, you should track
37332 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
37333 * will not have to rebuild the DOM elements for items it has already rendered, even if the
37334 * JavaScript objects in the collection have been substituted for new ones. For large collections,
37335 * this significantly improves rendering performance. If you don't have a unique identifier,
37336 * `track by $index` can also provide a performance boost.
37339 * <div ng-repeat="model in collection track by model.id">
37344 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
37345 * `$id` function, which tracks items by their identity:
37347 * <div ng-repeat="obj in collection track by $id(obj)">
37352 * <div class="alert alert-warning">
37353 * **Note:** `track by` must always be the last expression:
37356 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
37361 * # Special repeat start and end points
37362 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
37363 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
37364 * 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)
37365 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
37367 * The example below makes use of this feature:
37369 * <header ng-repeat-start="item in items">
37370 * Header {{ item }}
37372 * <div class="body">
37375 * <footer ng-repeat-end>
37376 * Footer {{ item }}
37380 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
37385 * <div class="body">
37394 * <div class="body">
37402 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
37403 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
37406 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
37408 * **.leave** - when an item is removed from the list or when an item is filtered out
37410 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
37412 * See the example below for defining CSS animations with ngRepeat.
37417 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
37418 * formats are currently supported:
37420 * * `variable in expression` – where variable is the user defined loop variable and `expression`
37421 * is a scope expression giving the collection to enumerate.
37423 * For example: `album in artist.albums`.
37425 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
37426 * and `expression` is the scope expression giving the collection to enumerate.
37428 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
37430 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
37431 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
37432 * is specified, ng-repeat associates elements by identity. It is an error to have
37433 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
37434 * mapped to the same DOM element, which is not possible.)
37436 * Note that the tracking expression must come last, after any filters, and the alias expression.
37438 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
37439 * will be associated by item identity in the array.
37441 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
37442 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
37443 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
37444 * element in the same way in the DOM.
37446 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
37447 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
37448 * property is same.
37450 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
37451 * to items in conjunction with a tracking expression.
37453 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
37454 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
37455 * when a filter is active on the repeater, but the filtered result set is empty.
37457 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
37458 * the items have been processed through the filter.
37460 * 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
37461 * (and not as operator, inside an expression).
37463 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
37466 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
37467 * results by name. New (entering) and removed (leaving) items are animated.
37468 <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
37469 <file name="index.html">
37470 <div ng-controller="repeatController">
37471 I have {{friends.length}} friends. They are:
37472 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
37473 <ul class="example-animate-container">
37474 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
37475 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
37477 <li class="animate-repeat" ng-if="results.length == 0">
37478 <strong>No results found...</strong>
37483 <file name="script.js">
37484 angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
37486 {name:'John', age:25, gender:'boy'},
37487 {name:'Jessie', age:30, gender:'girl'},
37488 {name:'Johanna', age:28, gender:'girl'},
37489 {name:'Joy', age:15, gender:'girl'},
37490 {name:'Mary', age:28, gender:'girl'},
37491 {name:'Peter', age:95, gender:'boy'},
37492 {name:'Sebastian', age:50, gender:'boy'},
37493 {name:'Erika', age:27, gender:'girl'},
37494 {name:'Patrick', age:40, gender:'boy'},
37495 {name:'Samantha', age:60, gender:'girl'}
37499 <file name="animations.css">
37500 .example-animate-container {
37502 border:1px solid black;
37511 box-sizing:border-box;
37514 .animate-repeat.ng-move,
37515 .animate-repeat.ng-enter,
37516 .animate-repeat.ng-leave {
37517 transition:all linear 0.5s;
37520 .animate-repeat.ng-leave.ng-leave-active,
37521 .animate-repeat.ng-move,
37522 .animate-repeat.ng-enter {
37527 .animate-repeat.ng-leave,
37528 .animate-repeat.ng-move.ng-move-active,
37529 .animate-repeat.ng-enter.ng-enter-active {
37534 <file name="protractor.js" type="protractor">
37535 var friends = element.all(by.repeater('friend in friends'));
37537 it('should render initial data set', function() {
37538 expect(friends.count()).toBe(10);
37539 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
37540 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
37541 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
37542 expect(element(by.binding('friends.length')).getText())
37543 .toMatch("I have 10 friends. They are:");
37546 it('should update repeater when filter predicate changes', function() {
37547 expect(friends.count()).toBe(10);
37549 element(by.model('q')).sendKeys('ma');
37551 expect(friends.count()).toBe(2);
37552 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
37553 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
37558 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
37559 var NG_REMOVED = '$$NG_REMOVED';
37560 var ngRepeatMinErr = minErr('ngRepeat');
37562 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
37563 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
37564 scope[valueIdentifier] = value;
37565 if (keyIdentifier) scope[keyIdentifier] = key;
37566 scope.$index = index;
37567 scope.$first = (index === 0);
37568 scope.$last = (index === (arrayLength - 1));
37569 scope.$middle = !(scope.$first || scope.$last);
37570 // jshint bitwise: false
37571 scope.$odd = !(scope.$even = (index&1) === 0);
37572 // jshint bitwise: true
37575 var getBlockStart = function(block) {
37576 return block.clone[0];
37579 var getBlockEnd = function(block) {
37580 return block.clone[block.clone.length - 1];
37586 multiElement: true,
37587 transclude: 'element',
37591 compile: function ngRepeatCompile($element, $attr) {
37592 var expression = $attr.ngRepeat;
37593 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
37595 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*$/);
37598 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
37602 var lhs = match[1];
37603 var rhs = match[2];
37604 var aliasAs = match[3];
37605 var trackByExp = match[4];
37607 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
37610 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
37613 var valueIdentifier = match[3] || match[1];
37614 var keyIdentifier = match[2];
37616 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
37617 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
37618 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
37622 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
37623 var hashFnLocals = {$id: hashKey};
37626 trackByExpGetter = $parse(trackByExp);
37628 trackByIdArrayFn = function(key, value) {
37629 return hashKey(value);
37631 trackByIdObjFn = function(key) {
37636 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
37638 if (trackByExpGetter) {
37639 trackByIdExpFn = function(key, value, index) {
37640 // assign key, value, and $index to the locals so that they can be used in hash functions
37641 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
37642 hashFnLocals[valueIdentifier] = value;
37643 hashFnLocals.$index = index;
37644 return trackByExpGetter($scope, hashFnLocals);
37648 // Store a list of elements from previous run. This is a hash where key is the item from the
37649 // iterator, and the value is objects with following properties.
37650 // - scope: bound scope
37651 // - element: previous element.
37652 // - index: position
37654 // We are using no-proto object so that we don't need to guard against inherited props via
37656 var lastBlockMap = createMap();
37659 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
37661 previousNode = $element[0], // node that cloned nodes should be inserted after
37662 // initialized to the comment node anchor
37664 // Same as lastBlockMap but it has the current state. It will become the
37665 // lastBlockMap on the next iteration.
37666 nextBlockMap = createMap(),
37668 key, value, // key/value of iteration
37672 block, // last object information {scope, element, id}
37677 $scope[aliasAs] = collection;
37680 if (isArrayLike(collection)) {
37681 collectionKeys = collection;
37682 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
37684 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
37685 // if object, extract keys, in enumeration order, unsorted
37686 collectionKeys = [];
37687 for (var itemKey in collection) {
37688 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
37689 collectionKeys.push(itemKey);
37694 collectionLength = collectionKeys.length;
37695 nextBlockOrder = new Array(collectionLength);
37697 // locate existing items
37698 for (index = 0; index < collectionLength; index++) {
37699 key = (collection === collectionKeys) ? index : collectionKeys[index];
37700 value = collection[key];
37701 trackById = trackByIdFn(key, value, index);
37702 if (lastBlockMap[trackById]) {
37703 // found previously seen block
37704 block = lastBlockMap[trackById];
37705 delete lastBlockMap[trackById];
37706 nextBlockMap[trackById] = block;
37707 nextBlockOrder[index] = block;
37708 } else if (nextBlockMap[trackById]) {
37709 // if collision detected. restore lastBlockMap and throw an error
37710 forEach(nextBlockOrder, function(block) {
37711 if (block && block.scope) lastBlockMap[block.id] = block;
37713 throw ngRepeatMinErr('dupes',
37714 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
37715 expression, trackById, value);
37717 // new never before seen block
37718 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
37719 nextBlockMap[trackById] = true;
37723 // remove leftover items
37724 for (var blockKey in lastBlockMap) {
37725 block = lastBlockMap[blockKey];
37726 elementsToRemove = getBlockNodes(block.clone);
37727 $animate.leave(elementsToRemove);
37728 if (elementsToRemove[0].parentNode) {
37729 // if the element was not removed yet because of pending animation, mark it as deleted
37730 // so that we can ignore it later
37731 for (index = 0, length = elementsToRemove.length; index < length; index++) {
37732 elementsToRemove[index][NG_REMOVED] = true;
37735 block.scope.$destroy();
37738 // we are not using forEach for perf reasons (trying to avoid #call)
37739 for (index = 0; index < collectionLength; index++) {
37740 key = (collection === collectionKeys) ? index : collectionKeys[index];
37741 value = collection[key];
37742 block = nextBlockOrder[index];
37745 // if we have already seen this object, then we need to reuse the
37746 // associated scope/element
37748 nextNode = previousNode;
37750 // skip nodes that are already pending removal via leave animation
37752 nextNode = nextNode.nextSibling;
37753 } while (nextNode && nextNode[NG_REMOVED]);
37755 if (getBlockStart(block) != nextNode) {
37756 // existing item which got moved
37757 $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
37759 previousNode = getBlockEnd(block);
37760 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
37762 // new item which we don't know about
37763 $transclude(function ngRepeatTransclude(clone, scope) {
37764 block.scope = scope;
37765 // http://jsperf.com/clone-vs-createcomment
37766 var endNode = ngRepeatEndComment.cloneNode(false);
37767 clone[clone.length++] = endNode;
37769 // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
37770 $animate.enter(clone, null, jqLite(previousNode));
37771 previousNode = endNode;
37772 // Note: We only need the first/last node of the cloned nodes.
37773 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
37774 // by a directive with templateUrl when its template arrives.
37775 block.clone = clone;
37776 nextBlockMap[block.id] = block;
37777 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
37781 lastBlockMap = nextBlockMap;
37788 var NG_HIDE_CLASS = 'ng-hide';
37789 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
37796 * The `ngShow` directive shows or hides the given HTML element based on the expression
37797 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
37798 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
37799 * in AngularJS and sets the display style to none (using an !important flag).
37800 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
37803 * <!-- when $scope.myValue is truthy (element is visible) -->
37804 * <div ng-show="myValue"></div>
37806 * <!-- when $scope.myValue is falsy (element is hidden) -->
37807 * <div ng-show="myValue" class="ng-hide"></div>
37810 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
37811 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
37812 * from the element causing the element not to appear hidden.
37814 * ## Why is !important used?
37816 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
37817 * can be easily overridden by heavier selectors. For example, something as simple
37818 * as changing the display style on a HTML list item would make hidden elements appear visible.
37819 * This also becomes a bigger issue when dealing with CSS frameworks.
37821 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
37822 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
37823 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
37825 * ### Overriding `.ng-hide`
37827 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
37828 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
37829 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
37830 * with extra animation classes that can be added.
37833 * .ng-hide:not(.ng-hide-animate) {
37834 * /* this is just another form of hiding an element */
37835 * display: block!important;
37836 * position: absolute;
37842 * By default you don't need to override in CSS anything and the animations will work around the display style.
37844 * ## A note about animations with `ngShow`
37846 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
37847 * is true and false. This system works like the animation system present with ngClass except that
37848 * you must also include the !important flag to override the display property
37849 * so that you can perform an animation when the element is hidden during the time of the animation.
37853 * //a working example can be found at the bottom of this page
37855 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
37856 * /* this is required as of 1.3x to properly
37857 * apply all styling in a show/hide animation */
37858 * transition: 0s linear all;
37861 * .my-element.ng-hide-add-active,
37862 * .my-element.ng-hide-remove-active {
37863 * /* the transition is defined in the active class */
37864 * transition: 1s linear all;
37867 * .my-element.ng-hide-add { ... }
37868 * .my-element.ng-hide-add.ng-hide-add-active { ... }
37869 * .my-element.ng-hide-remove { ... }
37870 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
37873 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
37874 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
37877 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
37878 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
37881 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
37882 * then the element is shown or hidden respectively.
37885 <example module="ngAnimate" deps="angular-animate.js" animations="true">
37886 <file name="index.html">
37887 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
37890 <div class="check-element animate-show" ng-show="checked">
37891 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
37896 <div class="check-element animate-show" ng-hide="checked">
37897 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
37901 <file name="glyphicons.css">
37902 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
37904 <file name="animations.css">
37909 border: 1px solid black;
37913 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
37914 transition: all linear 0.5s;
37917 .animate-show.ng-hide {
37925 border: 1px solid black;
37929 <file name="protractor.js" type="protractor">
37930 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
37931 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
37933 it('should check ng-show / ng-hide', function() {
37934 expect(thumbsUp.isDisplayed()).toBeFalsy();
37935 expect(thumbsDown.isDisplayed()).toBeTruthy();
37937 element(by.model('checked')).click();
37939 expect(thumbsUp.isDisplayed()).toBeTruthy();
37940 expect(thumbsDown.isDisplayed()).toBeFalsy();
37945 var ngShowDirective = ['$animate', function($animate) {
37948 multiElement: true,
37949 link: function(scope, element, attr) {
37950 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
37951 // we're adding a temporary, animation-specific class for ng-hide since this way
37952 // we can control when the element is actually displayed on screen without having
37953 // to have a global/greedy CSS selector that breaks when other animations are run.
37954 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
37955 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
37956 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
37970 * The `ngHide` directive shows or hides the given HTML element based on the expression
37971 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
37972 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
37973 * in AngularJS and sets the display style to none (using an !important flag).
37974 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
37977 * <!-- when $scope.myValue is truthy (element is hidden) -->
37978 * <div ng-hide="myValue" class="ng-hide"></div>
37980 * <!-- when $scope.myValue is falsy (element is visible) -->
37981 * <div ng-hide="myValue"></div>
37984 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
37985 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
37986 * from the element causing the element not to appear hidden.
37988 * ## Why is !important used?
37990 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
37991 * can be easily overridden by heavier selectors. For example, something as simple
37992 * as changing the display style on a HTML list item would make hidden elements appear visible.
37993 * This also becomes a bigger issue when dealing with CSS frameworks.
37995 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
37996 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
37997 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
37999 * ### Overriding `.ng-hide`
38001 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
38002 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
38007 * /* this is just another form of hiding an element */
38008 * display: block!important;
38009 * position: absolute;
38015 * By default you don't need to override in CSS anything and the animations will work around the display style.
38017 * ## A note about animations with `ngHide`
38019 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
38020 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
38021 * CSS class is added and removed for you instead of your own CSS class.
38025 * //a working example can be found at the bottom of this page
38027 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
38028 * transition: 0.5s linear all;
38031 * .my-element.ng-hide-add { ... }
38032 * .my-element.ng-hide-add.ng-hide-add-active { ... }
38033 * .my-element.ng-hide-remove { ... }
38034 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
38037 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
38038 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
38041 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
38042 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
38045 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
38046 * the element is shown or hidden respectively.
38049 <example module="ngAnimate" deps="angular-animate.js" animations="true">
38050 <file name="index.html">
38051 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
38054 <div class="check-element animate-hide" ng-show="checked">
38055 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
38060 <div class="check-element animate-hide" ng-hide="checked">
38061 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
38065 <file name="glyphicons.css">
38066 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
38068 <file name="animations.css">
38070 transition: all linear 0.5s;
38074 border: 1px solid black;
38078 .animate-hide.ng-hide {
38086 border: 1px solid black;
38090 <file name="protractor.js" type="protractor">
38091 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
38092 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
38094 it('should check ng-show / ng-hide', function() {
38095 expect(thumbsUp.isDisplayed()).toBeFalsy();
38096 expect(thumbsDown.isDisplayed()).toBeTruthy();
38098 element(by.model('checked')).click();
38100 expect(thumbsUp.isDisplayed()).toBeTruthy();
38101 expect(thumbsDown.isDisplayed()).toBeFalsy();
38106 var ngHideDirective = ['$animate', function($animate) {
38109 multiElement: true,
38110 link: function(scope, element, attr) {
38111 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
38112 // The comment inside of the ngShowDirective explains why we add and
38113 // remove a temporary class for the show/hide animation
38114 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
38115 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
38128 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
38131 * @param {expression} ngStyle
38133 * {@link guide/expression Expression} which evals to an
38134 * object whose keys are CSS style names and values are corresponding values for those CSS
38137 * Since some CSS style names are not valid keys for an object, they must be quoted.
38138 * See the 'background-color' style in the example below.
38142 <file name="index.html">
38143 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
38144 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
38145 <input type="button" value="clear" ng-click="myStyle={}">
38147 <span ng-style="myStyle">Sample Text</span>
38148 <pre>myStyle={{myStyle}}</pre>
38150 <file name="style.css">
38155 <file name="protractor.js" type="protractor">
38156 var colorSpan = element(by.css('span'));
38158 it('should check ng-style', function() {
38159 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
38160 element(by.css('input[value=\'set color\']')).click();
38161 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
38162 element(by.css('input[value=clear]')).click();
38163 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
38168 var ngStyleDirective = ngDirective(function(scope, element, attr) {
38169 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
38170 if (oldStyles && (newStyles !== oldStyles)) {
38171 forEach(oldStyles, function(val, style) { element.css(style, '');});
38173 if (newStyles) element.css(newStyles);
38183 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
38184 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
38185 * as specified in the template.
38187 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
38188 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
38189 * matches the value obtained from the evaluated expression. In other words, you define a container element
38190 * (where you place the directive), place an expression on the **`on="..."` attribute**
38191 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
38192 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
38193 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
38194 * attribute is displayed.
38196 * <div class="alert alert-info">
38197 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
38198 * as literal string values to match against.
38199 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
38200 * value of the expression `$scope.someVal`.
38204 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
38205 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
38210 * <ANY ng-switch="expression">
38211 * <ANY ng-switch-when="matchValue1">...</ANY>
38212 * <ANY ng-switch-when="matchValue2">...</ANY>
38213 * <ANY ng-switch-default>...</ANY>
38220 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
38221 * On child elements add:
38223 * * `ngSwitchWhen`: the case statement to match against. If match then this
38224 * case will be displayed. If the same match appears multiple times, all the
38225 * elements will be displayed.
38226 * * `ngSwitchDefault`: the default case when no other case match. If there
38227 * are multiple default cases, all of them will be displayed when no other
38232 <example module="switchExample" deps="angular-animate.js" animations="true">
38233 <file name="index.html">
38234 <div ng-controller="ExampleController">
38235 <select ng-model="selection" ng-options="item for item in items">
38237 <code>selection={{selection}}</code>
38239 <div class="animate-switch-container"
38240 ng-switch on="selection">
38241 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
38242 <div class="animate-switch" ng-switch-when="home">Home Span</div>
38243 <div class="animate-switch" ng-switch-default>default</div>
38247 <file name="script.js">
38248 angular.module('switchExample', ['ngAnimate'])
38249 .controller('ExampleController', ['$scope', function($scope) {
38250 $scope.items = ['settings', 'home', 'other'];
38251 $scope.selection = $scope.items[0];
38254 <file name="animations.css">
38255 .animate-switch-container {
38258 border:1px solid black;
38267 .animate-switch.ng-animate {
38268 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
38277 .animate-switch.ng-leave.ng-leave-active,
38278 .animate-switch.ng-enter {
38281 .animate-switch.ng-leave,
38282 .animate-switch.ng-enter.ng-enter-active {
38286 <file name="protractor.js" type="protractor">
38287 var switchElem = element(by.css('[ng-switch]'));
38288 var select = element(by.model('selection'));
38290 it('should start in settings', function() {
38291 expect(switchElem.getText()).toMatch(/Settings Div/);
38293 it('should change to home', function() {
38294 select.all(by.css('option')).get(1).click();
38295 expect(switchElem.getText()).toMatch(/Home Span/);
38297 it('should select default', function() {
38298 select.all(by.css('option')).get(2).click();
38299 expect(switchElem.getText()).toMatch(/default/);
38304 var ngSwitchDirective = ['$animate', function($animate) {
38306 require: 'ngSwitch',
38308 // asks for $scope to fool the BC controller module
38309 controller: ['$scope', function ngSwitchController() {
38312 link: function(scope, element, attr, ngSwitchController) {
38313 var watchExpr = attr.ngSwitch || attr.on,
38314 selectedTranscludes = [],
38315 selectedElements = [],
38316 previousLeaveAnimations = [],
38317 selectedScopes = [];
38319 var spliceFactory = function(array, index) {
38320 return function() { array.splice(index, 1); };
38323 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
38325 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
38326 $animate.cancel(previousLeaveAnimations[i]);
38328 previousLeaveAnimations.length = 0;
38330 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
38331 var selected = getBlockNodes(selectedElements[i].clone);
38332 selectedScopes[i].$destroy();
38333 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
38334 promise.then(spliceFactory(previousLeaveAnimations, i));
38337 selectedElements.length = 0;
38338 selectedScopes.length = 0;
38340 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
38341 forEach(selectedTranscludes, function(selectedTransclude) {
38342 selectedTransclude.transclude(function(caseElement, selectedScope) {
38343 selectedScopes.push(selectedScope);
38344 var anchor = selectedTransclude.element;
38345 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
38346 var block = { clone: caseElement };
38348 selectedElements.push(block);
38349 $animate.enter(caseElement, anchor.parent(), anchor);
38358 var ngSwitchWhenDirective = ngDirective({
38359 transclude: 'element',
38361 require: '^ngSwitch',
38362 multiElement: true,
38363 link: function(scope, element, attrs, ctrl, $transclude) {
38364 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
38365 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
38369 var ngSwitchDefaultDirective = ngDirective({
38370 transclude: 'element',
38372 require: '^ngSwitch',
38373 multiElement: true,
38374 link: function(scope, element, attr, ctrl, $transclude) {
38375 ctrl.cases['?'] = (ctrl.cases['?'] || []);
38376 ctrl.cases['?'].push({ transclude: $transclude, element: element });
38382 * @name ngTransclude
38386 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
38388 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
38389 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
38391 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
38392 * content of this element will be removed before the transcluded content is inserted.
38393 * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
38394 * that no transcluded content is provided.
38398 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
38399 * or its value is the same as the name of the attribute then the default slot is used.
38402 * ### Basic transclusion
38403 * This example demonstrates basic transclusion of content into a component directive.
38404 * <example name="simpleTranscludeExample" module="transcludeExample">
38405 * <file name="index.html">
38407 * angular.module('transcludeExample', [])
38408 * .directive('pane', function(){
38411 * transclude: true,
38412 * scope: { title:'@' },
38413 * template: '<div style="border: 1px solid black;">' +
38414 * '<div style="background-color: gray">{{title}}</div>' +
38415 * '<ng-transclude></ng-transclude>' +
38419 * .controller('ExampleController', ['$scope', function($scope) {
38420 * $scope.title = 'Lorem Ipsum';
38421 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
38424 * <div ng-controller="ExampleController">
38425 * <input ng-model="title" aria-label="title"> <br/>
38426 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
38427 * <pane title="{{title}}">{{text}}</pane>
38430 * <file name="protractor.js" type="protractor">
38431 * it('should have transcluded', function() {
38432 * var titleElement = element(by.model('title'));
38433 * titleElement.clear();
38434 * titleElement.sendKeys('TITLE');
38435 * var textElement = element(by.model('text'));
38436 * textElement.clear();
38437 * textElement.sendKeys('TEXT');
38438 * expect(element(by.binding('title')).getText()).toEqual('TITLE');
38439 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
38445 * ### Transclude fallback content
38446 * This example shows how to use `NgTransclude` with fallback content, that
38447 * is displayed if no transcluded content is provided.
38449 * <example module="transcludeFallbackContentExample">
38450 * <file name="index.html">
38452 * angular.module('transcludeFallbackContentExample', [])
38453 * .directive('myButton', function(){
38456 * transclude: true,
38458 * template: '<button style="cursor: pointer;">' +
38459 * '<ng-transclude>' +
38460 * '<b style="color: red;">Button1</b>' +
38461 * '</ng-transclude>' +
38466 * <!-- fallback button content -->
38467 * <my-button id="fallback"></my-button>
38468 * <!-- modified button content -->
38469 * <my-button id="modified">
38470 * <i style="color: green;">Button2</i>
38473 * <file name="protractor.js" type="protractor">
38474 * it('should have different transclude element content', function() {
38475 * expect(element(by.id('fallback')).getText()).toBe('Button1');
38476 * expect(element(by.id('modified')).getText()).toBe('Button2');
38482 * ### Multi-slot transclusion
38483 * This example demonstrates using multi-slot transclusion in a component directive.
38484 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
38485 * <file name="index.html">
38487 * .title, .footer {
38488 * background-color: gray
38491 * <div ng-controller="ExampleController">
38492 * <input ng-model="title" aria-label="title"> <br/>
38493 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
38495 * <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
38496 * <pane-body><p>{{text}}</p></pane-body>
38500 * <file name="app.js">
38501 * angular.module('multiSlotTranscludeExample', [])
38502 * .directive('pane', function(){
38506 * 'title': '?paneTitle',
38507 * 'body': 'paneBody',
38508 * 'footer': '?paneFooter'
38510 * template: '<div style="border: 1px solid black;">' +
38511 * '<div class="title" ng-transclude="title">Fallback Title</div>' +
38512 * '<div ng-transclude="body"></div>' +
38513 * '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
38517 * .controller('ExampleController', ['$scope', function($scope) {
38518 * $scope.title = 'Lorem Ipsum';
38519 * $scope.link = "https://google.com";
38520 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
38523 * <file name="protractor.js" type="protractor">
38524 * it('should have transcluded the title and the body', function() {
38525 * var titleElement = element(by.model('title'));
38526 * titleElement.clear();
38527 * titleElement.sendKeys('TITLE');
38528 * var textElement = element(by.model('text'));
38529 * textElement.clear();
38530 * textElement.sendKeys('TEXT');
38531 * expect(element(by.css('.title')).getText()).toEqual('TITLE');
38532 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
38533 * expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
38538 var ngTranscludeMinErr = minErr('ngTransclude');
38539 var ngTranscludeDirective = ngDirective({
38541 link: function($scope, $element, $attrs, controller, $transclude) {
38543 if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
38544 // If the attribute is of the form: `ng-transclude="ng-transclude"`
38545 // then treat it like the default
38546 $attrs.ngTransclude = '';
38549 function ngTranscludeCloneAttachFn(clone) {
38550 if (clone.length) {
38552 $element.append(clone);
38556 if (!$transclude) {
38557 throw ngTranscludeMinErr('orphan',
38558 'Illegal use of ngTransclude directive in the template! ' +
38559 'No parent directive that requires a transclusion found. ' +
38561 startingTag($element));
38564 // If there is no slot name defined or the slot name is not optional
38565 // then transclude the slot
38566 var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
38567 $transclude(ngTranscludeCloneAttachFn, null, slotName);
38577 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
38578 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
38579 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
38580 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
38581 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
38583 * @param {string} type Must be set to `'text/ng-template'`.
38584 * @param {string} id Cache name of the template.
38588 <file name="index.html">
38589 <script type="text/ng-template" id="/tpl.html">
38590 Content of the template.
38593 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
38594 <div id="tpl-content" ng-include src="currentTpl"></div>
38596 <file name="protractor.js" type="protractor">
38597 it('should load template defined inside script tag', function() {
38598 element(by.css('#tpl-link')).click();
38599 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
38604 var scriptDirective = ['$templateCache', function($templateCache) {
38608 compile: function(element, attr) {
38609 if (attr.type == 'text/ng-template') {
38610 var templateUrl = attr.id,
38611 text = element[0].text;
38613 $templateCache.put(templateUrl, text);
38619 var noopNgModelController = { $setViewValue: noop, $render: noop };
38621 function chromeHack(optionElement) {
38622 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
38623 // Adding an <option selected="selected"> element to a <select required="required"> should
38624 // automatically select the new element
38625 if (optionElement[0].hasAttribute('selected')) {
38626 optionElement[0].selected = true;
38632 * @name select.SelectController
38634 * The controller for the `<select>` directive. This provides support for reading
38635 * and writing the selected value(s) of the control and also coordinates dynamically
38636 * added `<option>` elements, perhaps by an `ngRepeat` directive.
38638 var SelectController =
38639 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
38642 optionsMap = new HashMap();
38644 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
38645 self.ngModelCtrl = noopNgModelController;
38647 // The "unknown" option is one that is prepended to the list if the viewValue
38648 // does not match any of the options. When it is rendered the value of the unknown
38649 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
38651 // We can't just jqLite('<option>') since jqLite is not smart enough
38652 // to create it in <select> and IE barfs otherwise.
38653 self.unknownOption = jqLite(document.createElement('option'));
38654 self.renderUnknownOption = function(val) {
38655 var unknownVal = '? ' + hashKey(val) + ' ?';
38656 self.unknownOption.val(unknownVal);
38657 $element.prepend(self.unknownOption);
38658 $element.val(unknownVal);
38661 $scope.$on('$destroy', function() {
38662 // disable unknown option so that we don't do work when the whole select is being destroyed
38663 self.renderUnknownOption = noop;
38666 self.removeUnknownOption = function() {
38667 if (self.unknownOption.parent()) self.unknownOption.remove();
38671 // Read the value of the select control, the implementation of this changes depending
38672 // upon whether the select can have multiple values and whether ngOptions is at work.
38673 self.readValue = function readSingleValue() {
38674 self.removeUnknownOption();
38675 return $element.val();
38679 // Write the value to the select control, the implementation of this changes depending
38680 // upon whether the select can have multiple values and whether ngOptions is at work.
38681 self.writeValue = function writeSingleValue(value) {
38682 if (self.hasOption(value)) {
38683 self.removeUnknownOption();
38684 $element.val(value);
38685 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
38687 if (value == null && self.emptyOption) {
38688 self.removeUnknownOption();
38691 self.renderUnknownOption(value);
38697 // Tell the select control that an option, with the given value, has been added
38698 self.addOption = function(value, element) {
38699 // Skip comment nodes, as they only pollute the `optionsMap`
38700 if (element[0].nodeType === NODE_TYPE_COMMENT) return;
38702 assertNotHasOwnProperty(value, '"option value"');
38703 if (value === '') {
38704 self.emptyOption = element;
38706 var count = optionsMap.get(value) || 0;
38707 optionsMap.put(value, count + 1);
38708 self.ngModelCtrl.$render();
38709 chromeHack(element);
38712 // Tell the select control that an option, with the given value, has been removed
38713 self.removeOption = function(value) {
38714 var count = optionsMap.get(value);
38717 optionsMap.remove(value);
38718 if (value === '') {
38719 self.emptyOption = undefined;
38722 optionsMap.put(value, count - 1);
38727 // Check whether the select control has an option matching the given value
38728 self.hasOption = function(value) {
38729 return !!optionsMap.get(value);
38733 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
38735 if (interpolateValueFn) {
38736 // The value attribute is interpolated
38738 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
38739 if (isDefined(oldVal)) {
38740 self.removeOption(oldVal);
38743 self.addOption(newVal, optionElement);
38745 } else if (interpolateTextFn) {
38746 // The text content is interpolated
38747 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
38748 optionAttrs.$set('value', newVal);
38749 if (oldVal !== newVal) {
38750 self.removeOption(oldVal);
38752 self.addOption(newVal, optionElement);
38755 // The value attribute is static
38756 self.addOption(optionAttrs.value, optionElement);
38759 optionElement.on('$destroy', function() {
38760 self.removeOption(optionAttrs.value);
38761 self.ngModelCtrl.$render();
38772 * HTML `SELECT` element with angular data-binding.
38774 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
38775 * between the scope and the `<select>` control (including setting default values).
38776 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
38777 * {@link ngOptions `ngOptions`} directives.
38779 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
38780 * to the model identified by the `ngModel` directive. With static or repeated options, this is
38781 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
38782 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
38784 * <div class="alert alert-warning">
38785 * Note that the value of a `select` directive used without `ngOptions` is always a string.
38786 * When the model needs to be bound to a non-string value, you must either explicitly convert it
38787 * using a directive (see example below) or use `ngOptions` to specify the set of options.
38788 * This is because an option element can only be bound to string values at present.
38791 * If the viewValue of `ngModel` does not match any of the options, then the control
38792 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
38794 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
38795 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
38796 * option. See example below for demonstration.
38798 * <div class="alert alert-info">
38799 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
38800 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
38801 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
38802 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
38803 * a new scope for each repeated instance.
38807 * @param {string} ngModel Assignable angular expression to data-bind to.
38808 * @param {string=} name Property name of the form under which the control is published.
38809 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
38810 * bound to the model as an array.
38811 * @param {string=} required Sets `required` validation error key if the value is not entered.
38812 * @param {string=} ngRequired Adds required attribute and required validation constraint to
38813 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
38814 * when you want to data-bind to the required attribute.
38815 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
38816 * interaction with the select element.
38817 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
38818 * set on the model on selection. See {@link ngOptions `ngOptions`}.
38821 * ### Simple `select` elements with static options
38823 * <example name="static-select" module="staticSelect">
38824 * <file name="index.html">
38825 * <div ng-controller="ExampleController">
38826 * <form name="myForm">
38827 * <label for="singleSelect"> Single select: </label><br>
38828 * <select name="singleSelect" ng-model="data.singleSelect">
38829 * <option value="option-1">Option 1</option>
38830 * <option value="option-2">Option 2</option>
38833 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
38834 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
38835 * <option value="">---Please select---</option> <!-- not selected / blank option -->
38836 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
38837 * <option value="option-2">Option 2</option>
38839 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
38840 * <tt>singleSelect = {{data.singleSelect}}</tt>
38843 * <label for="multipleSelect"> Multiple select: </label><br>
38844 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
38845 * <option value="option-1">Option 1</option>
38846 * <option value="option-2">Option 2</option>
38847 * <option value="option-3">Option 3</option>
38849 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
38853 * <file name="app.js">
38854 * angular.module('staticSelect', [])
38855 * .controller('ExampleController', ['$scope', function($scope) {
38857 * singleSelect: null,
38858 * multipleSelect: [],
38859 * option1: 'option-1',
38862 * $scope.forceUnknownOption = function() {
38863 * $scope.data.singleSelect = 'nonsense';
38869 * ### Using `ngRepeat` to generate `select` options
38870 * <example name="ngrepeat-select" module="ngrepeatSelect">
38871 * <file name="index.html">
38872 * <div ng-controller="ExampleController">
38873 * <form name="myForm">
38874 * <label for="repeatSelect"> Repeat select: </label>
38875 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
38876 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
38880 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
38883 * <file name="app.js">
38884 * angular.module('ngrepeatSelect', [])
38885 * .controller('ExampleController', ['$scope', function($scope) {
38887 * repeatSelect: null,
38888 * availableOptions: [
38889 * {id: '1', name: 'Option A'},
38890 * {id: '2', name: 'Option B'},
38891 * {id: '3', name: 'Option C'}
38899 * ### Using `select` with `ngOptions` and setting a default value
38900 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
38902 * <example name="select-with-default-values" module="defaultValueSelect">
38903 * <file name="index.html">
38904 * <div ng-controller="ExampleController">
38905 * <form name="myForm">
38906 * <label for="mySelect">Make a choice:</label>
38907 * <select name="mySelect" id="mySelect"
38908 * ng-options="option.name for option in data.availableOptions track by option.id"
38909 * ng-model="data.selectedOption"></select>
38912 * <tt>option = {{data.selectedOption}}</tt><br/>
38915 * <file name="app.js">
38916 * angular.module('defaultValueSelect', [])
38917 * .controller('ExampleController', ['$scope', function($scope) {
38919 * availableOptions: [
38920 * {id: '1', name: 'Option A'},
38921 * {id: '2', name: 'Option B'},
38922 * {id: '3', name: 'Option C'}
38924 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
38931 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
38933 * <example name="select-with-non-string-options" module="nonStringSelect">
38934 * <file name="index.html">
38935 * <select ng-model="model.id" convert-to-number>
38936 * <option value="0">Zero</option>
38937 * <option value="1">One</option>
38938 * <option value="2">Two</option>
38942 * <file name="app.js">
38943 * angular.module('nonStringSelect', [])
38944 * .run(function($rootScope) {
38945 * $rootScope.model = { id: 2 };
38947 * .directive('convertToNumber', function() {
38949 * require: 'ngModel',
38950 * link: function(scope, element, attrs, ngModel) {
38951 * ngModel.$parsers.push(function(val) {
38952 * return parseInt(val, 10);
38954 * ngModel.$formatters.push(function(val) {
38961 * <file name="protractor.js" type="protractor">
38962 * it('should initialize to model', function() {
38963 * var select = element(by.css('select'));
38964 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
38970 var selectDirective = function() {
38974 require: ['select', '?ngModel'],
38975 controller: SelectController,
38978 pre: selectPreLink,
38979 post: selectPostLink
38983 function selectPreLink(scope, element, attr, ctrls) {
38985 // if ngModel is not defined, we don't need to do anything
38986 var ngModelCtrl = ctrls[1];
38987 if (!ngModelCtrl) return;
38989 var selectCtrl = ctrls[0];
38991 selectCtrl.ngModelCtrl = ngModelCtrl;
38993 // When the selected item(s) changes we delegate getting the value of the select control
38994 // to the `readValue` method, which can be changed if the select can have multiple
38995 // selected values or if the options are being generated by `ngOptions`
38996 element.on('change', function() {
38997 scope.$apply(function() {
38998 ngModelCtrl.$setViewValue(selectCtrl.readValue());
39002 // If the select allows multiple values then we need to modify how we read and write
39003 // values from and to the control; also what it means for the value to be empty and
39004 // we have to add an extra watch since ngModel doesn't work well with arrays - it
39005 // doesn't trigger rendering if only an item in the array changes.
39006 if (attr.multiple) {
39008 // Read value now needs to check each option to see if it is selected
39009 selectCtrl.readValue = function readMultipleValue() {
39011 forEach(element.find('option'), function(option) {
39012 if (option.selected) {
39013 array.push(option.value);
39019 // Write value now needs to set the selected property of each matching option
39020 selectCtrl.writeValue = function writeMultipleValue(value) {
39021 var items = new HashMap(value);
39022 forEach(element.find('option'), function(option) {
39023 option.selected = isDefined(items.get(option.value));
39027 // we have to do it on each watch since ngModel watches reference, but
39028 // we need to work of an array, so we need to see if anything was inserted/removed
39029 var lastView, lastViewRef = NaN;
39030 scope.$watch(function selectMultipleWatch() {
39031 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
39032 lastView = shallowCopy(ngModelCtrl.$viewValue);
39033 ngModelCtrl.$render();
39035 lastViewRef = ngModelCtrl.$viewValue;
39038 // If we are a multiple select then value is now a collection
39039 // so the meaning of $isEmpty changes
39040 ngModelCtrl.$isEmpty = function(value) {
39041 return !value || value.length === 0;
39047 function selectPostLink(scope, element, attrs, ctrls) {
39048 // if ngModel is not defined, we don't need to do anything
39049 var ngModelCtrl = ctrls[1];
39050 if (!ngModelCtrl) return;
39052 var selectCtrl = ctrls[0];
39054 // We delegate rendering to the `writeValue` method, which can be changed
39055 // if the select can have multiple selected values or if the options are being
39056 // generated by `ngOptions`.
39057 // This must be done in the postLink fn to prevent $render to be called before
39058 // all nodes have been linked correctly.
39059 ngModelCtrl.$render = function() {
39060 selectCtrl.writeValue(ngModelCtrl.$viewValue);
39066 // The option directive is purely designed to communicate the existence (or lack of)
39067 // of dynamically created (and destroyed) option elements to their containing select
39068 // directive via its controller.
39069 var optionDirective = ['$interpolate', function($interpolate) {
39073 compile: function(element, attr) {
39074 if (isDefined(attr.value)) {
39075 // If the value attribute is defined, check if it contains an interpolation
39076 var interpolateValueFn = $interpolate(attr.value, true);
39078 // If the value attribute is not defined then we fall back to the
39079 // text content of the option element, which may be interpolated
39080 var interpolateTextFn = $interpolate(element.text(), true);
39081 if (!interpolateTextFn) {
39082 attr.$set('value', element.text());
39086 return function(scope, element, attr) {
39087 // This is an optimization over using ^^ since we don't want to have to search
39088 // all the way to the root of the DOM for every single option element
39089 var selectCtrlName = '$selectController',
39090 parent = element.parent(),
39091 selectCtrl = parent.data(selectCtrlName) ||
39092 parent.parent().data(selectCtrlName); // in case we are in optgroup
39095 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
39102 var styleDirective = valueFn({
39113 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39114 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
39115 * applied to custom controls.
39117 * The directive sets the `required` attribute on the element if the Angular expression inside
39118 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
39119 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
39122 * The validator will set the `required` error key to true if the `required` attribute is set and
39123 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
39124 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
39125 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
39126 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
39129 * <example name="ngRequiredDirective" module="ngRequiredExample">
39130 * <file name="index.html">
39132 * angular.module('ngRequiredExample', [])
39133 * .controller('ExampleController', ['$scope', function($scope) {
39134 * $scope.required = true;
39137 * <div ng-controller="ExampleController">
39138 * <form name="form">
39139 * <label for="required">Toggle required: </label>
39140 * <input type="checkbox" ng-model="required" id="required" />
39142 * <label for="input">This input must be filled if `required` is true: </label>
39143 * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
39145 * required error set? = <code>{{form.input.$error.required}}</code><br>
39146 * model = <code>{{model}}</code>
39150 * <file name="protractor.js" type="protractor">
39151 var required = element(by.binding('form.input.$error.required'));
39152 var model = element(by.binding('model'));
39153 var input = element(by.id('input'));
39155 it('should set the required error', function() {
39156 expect(required.getText()).toContain('true');
39158 input.sendKeys('123');
39159 expect(required.getText()).not.toContain('true');
39160 expect(model.getText()).toContain('123');
39165 var requiredDirective = function() {
39168 require: '?ngModel',
39169 link: function(scope, elm, attr, ctrl) {
39171 attr.required = true; // force truthy in case we are on non input element
39173 ctrl.$validators.required = function(modelValue, viewValue) {
39174 return !attr.required || !ctrl.$isEmpty(viewValue);
39177 attr.$observe('required', function() {
39190 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39191 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
39193 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
39194 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
39195 * `ngPattern` attribute value:
39196 * * If the expression evaluates to a RegExp object, then this is used directly.
39197 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
39198 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
39200 * <div class="alert alert-info">
39201 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
39202 * start at the index of the last search's match, thus not taking the whole input value into
39206 * <div class="alert alert-info">
39207 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
39211 * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
39215 * The `ngPattern` attribute must be an expression, while the `pattern` value must be
39222 * <example name="ngPatternDirective" module="ngPatternExample">
39223 * <file name="index.html">
39225 * angular.module('ngPatternExample', [])
39226 * .controller('ExampleController', ['$scope', function($scope) {
39227 * $scope.regex = '\\d+';
39230 * <div ng-controller="ExampleController">
39231 * <form name="form">
39232 * <label for="regex">Set a pattern (regex string): </label>
39233 * <input type="text" ng-model="regex" id="regex" />
39235 * <label for="input">This input is restricted by the current pattern: </label>
39236 * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
39238 * input valid? = <code>{{form.input.$valid}}</code><br>
39239 * model = <code>{{model}}</code>
39243 * <file name="protractor.js" type="protractor">
39244 var model = element(by.binding('model'));
39245 var input = element(by.id('input'));
39247 it('should validate the input with the default pattern', function() {
39248 input.sendKeys('aaa');
39249 expect(model.getText()).not.toContain('aaa');
39251 input.clear().then(function() {
39252 input.sendKeys('123');
39253 expect(model.getText()).toContain('123');
39259 var patternDirective = function() {
39262 require: '?ngModel',
39263 link: function(scope, elm, attr, ctrl) {
39266 var regexp, patternExp = attr.ngPattern || attr.pattern;
39267 attr.$observe('pattern', function(regex) {
39268 if (isString(regex) && regex.length > 0) {
39269 regex = new RegExp('^' + regex + '$');
39272 if (regex && !regex.test) {
39273 throw minErr('ngPattern')('noregexp',
39274 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
39275 regex, startingTag(elm));
39278 regexp = regex || undefined;
39282 ctrl.$validators.pattern = function(modelValue, viewValue) {
39283 // HTML5 pattern constraint validates the input value, so we validate the viewValue
39284 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
39292 * @name ngMaxlength
39296 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39297 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
39299 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
39300 * is longer than the integer obtained by evaluating the Angular expression given in the
39301 * `ngMaxlength` attribute value.
39303 * <div class="alert alert-info">
39304 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
39308 * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
39309 * validation is not available.
39312 * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
39319 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
39320 * <file name="index.html">
39322 * angular.module('ngMaxlengthExample', [])
39323 * .controller('ExampleController', ['$scope', function($scope) {
39324 * $scope.maxlength = 5;
39327 * <div ng-controller="ExampleController">
39328 * <form name="form">
39329 * <label for="maxlength">Set a maxlength: </label>
39330 * <input type="number" ng-model="maxlength" id="maxlength" />
39332 * <label for="input">This input is restricted by the current maxlength: </label>
39333 * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
39335 * input valid? = <code>{{form.input.$valid}}</code><br>
39336 * model = <code>{{model}}</code>
39340 * <file name="protractor.js" type="protractor">
39341 var model = element(by.binding('model'));
39342 var input = element(by.id('input'));
39344 it('should validate the input with the default maxlength', function() {
39345 input.sendKeys('abcdef');
39346 expect(model.getText()).not.toContain('abcdef');
39348 input.clear().then(function() {
39349 input.sendKeys('abcde');
39350 expect(model.getText()).toContain('abcde');
39356 var maxlengthDirective = function() {
39359 require: '?ngModel',
39360 link: function(scope, elm, attr, ctrl) {
39363 var maxlength = -1;
39364 attr.$observe('maxlength', function(value) {
39365 var intVal = toInt(value);
39366 maxlength = isNaN(intVal) ? -1 : intVal;
39369 ctrl.$validators.maxlength = function(modelValue, viewValue) {
39370 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
39378 * @name ngMinlength
39382 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
39383 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
39385 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
39386 * is shorter than the integer obtained by evaluating the Angular expression given in the
39387 * `ngMinlength` attribute value.
39389 * <div class="alert alert-info">
39390 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
39394 * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
39395 * validation is not available.
39398 * The `ngMinlength` value must be an expression, while the `minlength` value must be
39405 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
39406 * <file name="index.html">
39408 * angular.module('ngMinlengthExample', [])
39409 * .controller('ExampleController', ['$scope', function($scope) {
39410 * $scope.minlength = 3;
39413 * <div ng-controller="ExampleController">
39414 * <form name="form">
39415 * <label for="minlength">Set a minlength: </label>
39416 * <input type="number" ng-model="minlength" id="minlength" />
39418 * <label for="input">This input is restricted by the current minlength: </label>
39419 * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
39421 * input valid? = <code>{{form.input.$valid}}</code><br>
39422 * model = <code>{{model}}</code>
39426 * <file name="protractor.js" type="protractor">
39427 var model = element(by.binding('model'));
39428 var input = element(by.id('input'));
39430 it('should validate the input with the default minlength', function() {
39431 input.sendKeys('ab');
39432 expect(model.getText()).not.toContain('ab');
39434 input.sendKeys('abc');
39435 expect(model.getText()).toContain('abc');
39440 var minlengthDirective = function() {
39443 require: '?ngModel',
39444 link: function(scope, elm, attr, ctrl) {
39448 attr.$observe('minlength', function(value) {
39449 minlength = toInt(value) || 0;
39452 ctrl.$validators.minlength = function(modelValue, viewValue) {
39453 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
39459 if (window.angular.bootstrap) {
39460 //AngularJS is already loaded, so we can return here...
39461 console.log('WARNING: Tried to load angular more than once.');
39465 //try to bind to jquery now so that one can write jqLite(document).ready()
39466 //but we will rebind on bootstrap again.
39469 publishExternalAPI(angular);
39471 angular.module("ngLocale", [], ["$provide", function($provide) {
39472 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
39473 function getDecimals(n) {
39475 var i = n.indexOf('.');
39476 return (i == -1) ? 0 : n.length - i - 1;
39479 function getVF(n, opt_precision) {
39480 var v = opt_precision;
39482 if (undefined === v) {
39483 v = Math.min(getDecimals(n), 3);
39486 var base = Math.pow(10, v);
39487 var f = ((n * base) | 0) % base;
39488 return {v: v, f: f};
39491 $provide.value("$locale", {
39492 "DATETIME_FORMATS": {
39514 "FIRSTDAYOFWEEK": 6,
39552 "STANDALONEMONTH": [
39570 "fullDate": "EEEE, MMMM d, y",
39571 "longDate": "MMMM d, y",
39572 "medium": "MMM d, y h:mm:ss a",
39573 "mediumDate": "MMM d, y",
39574 "mediumTime": "h:mm:ss a",
39575 "short": "M/d/yy h:mm a",
39576 "shortDate": "M/d/yy",
39577 "shortTime": "h:mm a"
39579 "NUMBER_FORMATS": {
39580 "CURRENCY_SYM": "$",
39581 "DECIMAL_SEP": ".",
39601 "negPre": "-\u00a4",
39603 "posPre": "\u00a4",
39609 "localeID": "en_US",
39610 "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;}
39615 * Setup file for the Scenario.
39616 * Must be first in the compilation/bootstrap list.
39619 // Public namespace
39620 angular.scenario = angular.scenario || {};
39623 * Expose jQuery (e.g. for custom dsl extensions).
39625 angular.scenario.jQuery = _jQuery;
39628 * Defines a new output format.
39630 * @param {string} name the name of the new output format
39631 * @param {function()} fn function(context, runner) that generates the output
39633 angular.scenario.output = angular.scenario.output || function(name, fn) {
39634 angular.scenario.output[name] = fn;
39638 * Defines a new DSL statement. If your factory function returns a Future
39639 * it's returned, otherwise the result is assumed to be a map of functions
39640 * for chaining. Chained functions are subject to the same rules.
39642 * Note: All functions on the chain are bound to the chain scope so values
39643 * set on "this" in your statement function are available in the chained
39646 * @param {string} name The name of the statement
39647 * @param {function()} fn Factory function(), return a function for
39650 angular.scenario.dsl = angular.scenario.dsl || function(name, fn) {
39651 angular.scenario.dsl[name] = function() {
39652 /* jshint -W040 *//* The dsl binds `this` for us when calling chained functions */
39653 function executeStatement(statement, args) {
39654 var result = statement.apply(this, args);
39655 if (angular.isFunction(result) || result instanceof angular.scenario.Future) {
39659 var chain = angular.extend({}, result);
39660 angular.forEach(chain, function(value, name) {
39661 if (angular.isFunction(value)) {
39662 chain[name] = function() {
39663 return executeStatement.call(self, value, arguments);
39666 chain[name] = value;
39671 var statement = fn.apply(this, arguments);
39672 return function() {
39673 return executeStatement.call(this, statement, arguments);
39679 * Defines a new matcher for use with the expects() statement. The value
39680 * this.actual (like in Jasmine) is available in your matcher to compare
39681 * against. Your function should return a boolean. The future is automatically
39684 * @param {string} name The name of the matcher
39685 * @param {function()} fn The matching function(expected).
39687 angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
39688 angular.scenario.matcher[name] = function(expected) {
39689 var description = this.future.name +
39690 (this.inverse ? ' not ' : ' ') + name +
39691 ' ' + angular.toJson(expected);
39693 this.addFuture('expect ' + description,
39696 self.actual = self.future.value;
39697 if ((self.inverse && fn.call(self, expected)) ||
39698 (!self.inverse && !fn.call(self, expected))) {
39699 error = 'expected ' + description +
39700 ' but was ' + angular.toJson(self.actual);
39708 * Initialize the scenario runner and run !
39710 * Access global window and document object
39711 * Access $runner through closure
39713 * @param {Object=} config Config options
39715 angular.scenario.setUpAndRun = function(config) {
39716 var href = window.location.href;
39717 var body = _jQuery(document.body);
39719 var objModel = new angular.scenario.ObjectModel($runner);
39721 if (config && config.scenario_output) {
39722 output = config.scenario_output.split(',');
39725 angular.forEach(angular.scenario.output, function(fn, name) {
39726 if (!output.length || output.indexOf(name) != -1) {
39727 var context = body.append('<div></div>').find('div:last');
39728 context.attr('id', name);
39729 fn.call({}, context, $runner, objModel);
39733 if (!/^http/.test(href) && !/^https/.test(href)) {
39734 body.append('<p id="system-error"></p>');
39735 body.find('#system-error').text(
39736 'Scenario runner must be run using http or https. The protocol ' +
39737 href.split(':')[0] + ':// is not supported.'
39742 var appFrame = body.append('<div id="application"></div>').find('#application');
39743 var application = new angular.scenario.Application(appFrame);
39745 $runner.on('RunnerEnd', function() {
39746 appFrame.css('display', 'none');
39747 appFrame.find('iframe').attr('src', 'about:blank');
39750 $runner.on('RunnerError', function(error) {
39751 if (window.console) {
39752 console.log(formatException(error));
39754 // Do something for IE
39759 $runner.run(application);
39763 * Iterates through list with iterator function that must call the
39764 * continueFunction to continue iterating.
39766 * @param {Array} list list to iterate over
39767 * @param {function()} iterator Callback function(value, continueFunction)
39768 * @param {function()} done Callback function(error, result) called when
39769 * iteration finishes or an error occurs.
39771 function asyncForEach(list, iterator, done) {
39773 function loop(error, index) {
39774 if (index && index > i) {
39777 if (error || i >= list.length) {
39781 iterator(list[i++], loop);
39791 * Formats an exception into a string with the stack trace, but limits
39792 * to a specific line length.
39794 * @param {Object} error The exception to format, can be anything throwable
39795 * @param {Number=} [maxStackLines=5] max lines of the stack trace to include
39798 function formatException(error, maxStackLines) {
39799 maxStackLines = maxStackLines || 5;
39800 var message = error.toString();
39802 var stack = error.stack.split('\n');
39803 if (stack[0].indexOf(message) === -1) {
39805 stack.unshift(error.message);
39807 message = stack.slice(0, maxStackLines).join('\n');
39813 * Returns a function that gets the file name and line number from a
39814 * location in the stack if available based on the call site.
39816 * Note: this returns another function because accessing .stack is very
39817 * expensive in Chrome.
39819 * @param {Number} offset Number of stack lines to skip
39821 function callerFile(offset) {
39822 var error = new Error();
39824 return function() {
39825 var line = (error.stack || '').split('\n')[offset];
39827 // Clean up the stack trace line
39829 if (line.indexOf('@') !== -1) {
39831 line = line.substring(line.indexOf('@') + 1);
39834 line = line.substring(line.indexOf('(') + 1).replace(')', '');
39844 * Don't use the jQuery trigger method since it works incorrectly.
39846 * jQuery notifies listeners and then changes the state of a checkbox and
39847 * does not create a real browser event. A real click changes the state of
39848 * the checkbox and then notifies listeners.
39850 * To work around this we instead use our own handler that fires a real event.
39853 // We need a handle to the original trigger function for input tests.
39854 var parentTrigger = fn._originalTrigger = fn.trigger;
39855 fn.trigger = function(type) {
39856 if (/(click|change|keydown|blur|input|mousedown|mouseup)/.test(type)) {
39857 var processDefaults = [];
39858 this.each(function(index, node) {
39859 processDefaults.push(browserTrigger(node, type));
39862 // this is not compatible with jQuery - we return an array of returned values,
39863 // so that scenario runner know whether JS code has preventDefault() of the event or not...
39864 return processDefaults;
39866 return parentTrigger.apply(this, arguments);
39871 * Finds all bindings with the substring match of name and returns an
39872 * array of their values.
39874 * @param {string} bindExp The name to match
39875 * @return {Array.<string>} String of binding values
39877 _jQuery.fn.bindings = function(windowJquery, bindExp) {
39878 var result = [], match,
39879 bindSelector = '.ng-binding:visible';
39880 if (angular.isString(bindExp)) {
39881 bindExp = bindExp.replace(/\s/g, '');
39882 match = function(actualExp) {
39884 actualExp = actualExp.replace(/\s/g, '');
39885 if (actualExp == bindExp) return true;
39886 if (actualExp.indexOf(bindExp) === 0) {
39887 return actualExp.charAt(bindExp.length) == '|';
39891 } else if (bindExp) {
39892 match = function(actualExp) {
39893 return actualExp && bindExp.exec(actualExp);
39896 match = function(actualExp) {
39897 return !!actualExp;
39900 var selection = this.find(bindSelector);
39901 if (this.is(bindSelector)) {
39902 selection = selection.add(this);
39905 function push(value) {
39906 if (angular.isUndefined(value)) {
39908 } else if (typeof value !== 'string') {
39909 value = angular.toJson(value);
39911 result.push('' + value);
39914 selection.each(function() {
39915 var element = windowJquery(this),
39917 if (bindings = element.data('$binding')) {
39918 for (var expressions = [], binding, j=0, jj=bindings.length; j < jj; j++) {
39919 binding = bindings[j];
39921 if (binding.expressions) {
39922 expressions = binding.expressions;
39924 expressions = [binding];
39926 for (var scope, expression, i = 0, ii = expressions.length; i < ii; i++) {
39927 expression = expressions[i];
39928 if (match(expression)) {
39929 scope = scope || element.scope();
39930 push(scope.$eval(expression));
39941 * Triggers a browser event. Attempts to choose the right event if one is
39944 * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
39945 * @param {string} eventType Optional event type
39946 * @param {Object=} eventData An optional object which contains additional event data (such as x,y
39947 * coordinates, keys, etc...) that are passed into the event when triggered
39949 window.browserTrigger = function browserTrigger(element, eventType, eventData) {
39950 if (element && !element.nodeName) element = element[0];
39951 if (!element) return;
39953 eventData = eventData || {};
39954 var relatedTarget = eventData.relatedTarget || element;
39955 var keys = eventData.keys;
39956 var x = eventData.x;
39957 var y = eventData.y;
39959 var inputType = (element.type) ? element.type.toLowerCase() : null,
39960 nodeName = element.nodeName.toLowerCase();
39964 'textarea': 'change',
39965 'hidden': 'change',
39966 'password': 'change',
39971 'checkbox': 'click',
39973 'select-one': 'change',
39974 'select-multiple': 'change',
39975 '_default_': 'click'
39976 }[inputType || '_default_'];
39979 if (nodeName == 'option') {
39980 element.parentNode.value = element.value;
39981 element = element.parentNode;
39982 eventType = 'change';
39986 function pressed(key) {
39987 return keys.indexOf(key) !== -1;
39991 if (/transitionend/.test(eventType)) {
39992 if (window.WebKitTransitionEvent) {
39993 evnt = new WebKitTransitionEvent(eventType, eventData);
39994 evnt.initEvent(eventType, false, true);
39997 evnt = new TransitionEvent(eventType, eventData);
40000 evnt = document.createEvent('TransitionEvent');
40001 evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
40004 } else if (/animationend/.test(eventType)) {
40005 if (window.WebKitAnimationEvent) {
40006 evnt = new WebKitAnimationEvent(eventType, eventData);
40007 evnt.initEvent(eventType, false, true);
40010 evnt = new AnimationEvent(eventType, eventData);
40013 evnt = document.createEvent('AnimationEvent');
40014 evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
40017 } else if (/touch/.test(eventType) && supportsTouchEvents()) {
40018 evnt = createTouchEvent(element, eventType, x, y);
40020 evnt = document.createEvent('MouseEvents');
40023 evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
40024 pressed('alt'), pressed('shift'), pressed('meta'), 0, relatedTarget);
40027 /* we're unable to change the timeStamp value directly so this
40028 * is only here to allow for testing where the timeStamp value is
40030 evnt.$manualTimeStamp = eventData.timeStamp;
40034 var originalPreventDefault = evnt.preventDefault,
40035 appWindow = element.ownerDocument.defaultView,
40036 fakeProcessDefault = true,
40037 finalProcessDefault,
40038 angular = appWindow.angular || {};
40040 // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
40041 angular['ff-684208-preventDefault'] = false;
40042 evnt.preventDefault = function() {
40043 fakeProcessDefault = false;
40044 return originalPreventDefault.apply(evnt, arguments);
40047 element.dispatchEvent(evnt);
40048 finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
40050 delete angular['ff-684208-preventDefault'];
40052 return finalProcessDefault;
40055 function supportsTouchEvents() {
40056 if ('_cached' in supportsTouchEvents) {
40057 return supportsTouchEvents._cached;
40059 if (!document.createTouch || !document.createTouchList) {
40060 supportsTouchEvents._cached = false;
40064 document.createEvent('TouchEvent');
40066 supportsTouchEvents._cached = false;
40069 supportsTouchEvents._cached = true;
40073 function createTouchEvent(element, eventType, x, y) {
40074 var evnt = new Event(eventType);
40078 var touch = document.createTouch(window, element, Date.now(), x, y, x, y);
40079 var touches = document.createTouchList(touch);
40081 evnt.touches = touches;
40088 * Represents the application currently being tested and abstracts usage
40089 * of iframes or separate windows.
40091 * @param {Object} context jQuery wrapper around HTML context.
40093 angular.scenario.Application = function(context) {
40094 this.context = context;
40096 '<h2>Current URL: <a href="about:blank">None</a></h2>' +
40097 '<div id="test-frames"></div>'
40102 * Gets the jQuery collection of frames. Don't use this directly because
40103 * frames may go stale.
40106 * @return {Object} jQuery collection
40108 angular.scenario.Application.prototype.getFrame_ = function() {
40109 return this.context.find('#test-frames iframe:last');
40113 * Gets the window of the test runner frame. Always favor executeAction()
40114 * instead of this method since it prevents you from getting a stale window.
40117 * @return {Object} the window of the frame
40119 angular.scenario.Application.prototype.getWindow_ = function() {
40120 var contentWindow = this.getFrame_().prop('contentWindow');
40121 if (!contentWindow) {
40122 throw 'Frame window is not accessible.';
40124 return contentWindow;
40128 * Changes the location of the frame.
40130 * @param {string} url The URL. If it begins with a # then only the
40131 * hash of the page is changed.
40132 * @param {function()} loadFn function($window, $document) Called when frame loads.
40133 * @param {function()} errorFn function(error) Called if any error when loading.
40135 angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorFn) {
40137 var frame = self.getFrame_();
40138 //TODO(esprehn): Refactor to use rethrow()
40139 errorFn = errorFn || function(e) { throw e; };
40140 if (url === 'about:blank') {
40141 errorFn('Sandbox Error: Navigating to about:blank is not allowed.');
40142 } else if (url.charAt(0) === '#') {
40143 url = frame.attr('src').split('#')[0] + url;
40144 frame.attr('src', url);
40145 self.executeAction(loadFn);
40148 self.context.find('#test-frames').append('<iframe>');
40149 frame = self.getFrame_();
40151 frame.load(function() {
40154 var $window = self.getWindow_();
40156 if (!$window.angular) {
40157 self.executeAction(loadFn);
40161 if (!$window.angular.resumeBootstrap) {
40162 $window.angular.resumeDeferredBootstrap = resumeDeferredBootstrap;
40164 resumeDeferredBootstrap();
40171 function resumeDeferredBootstrap() {
40172 // Disable animations
40173 var $injector = $window.angular.resumeBootstrap([['$provide', function($provide) {
40174 return ['$animate', function($animate) {
40175 $animate.enabled(false);
40178 self.rootElement = $injector.get('$rootElement')[0];
40179 self.executeAction(loadFn);
40181 }).attr('src', url);
40183 // for IE compatibility set the name *after* setting the frame url
40184 frame[0].contentWindow.name = "NG_DEFER_BOOTSTRAP!";
40186 self.context.find('> h2 a').attr('href', url).text(url);
40190 * Executes a function in the context of the tested application. Will wait
40191 * for all pending angular xhr requests before executing.
40193 * @param {function()} action The callback to execute. function($window, $document)
40194 * $document is a jQuery wrapped document.
40196 angular.scenario.Application.prototype.executeAction = function(action) {
40198 var $window = this.getWindow_();
40199 if (!$window.document) {
40200 throw 'Sandbox Error: Application document not accessible.';
40202 if (!$window.angular) {
40203 return action.call(this, $window, _jQuery($window.document));
40206 if (!!this.rootElement) {
40207 executeWithElement(this.rootElement);
40209 angularInit($window.document, angular.bind(this, executeWithElement));
40212 function executeWithElement(element) {
40213 var $injector = $window.angular.element(element).injector();
40214 var $element = _jQuery(element);
40216 $element.injector = function() {
40220 $injector.invoke(function($browser) {
40221 $browser.notifyWhenNoOutstandingRequests(function() {
40222 action.call(self, $window, $element);
40229 * The representation of define blocks. Don't used directly, instead use
40230 * define() in your tests.
40232 * @param {string} descName Name of the block
40233 * @param {Object} parent describe or undefined if the root.
40235 angular.scenario.Describe = function(descName, parent) {
40236 this.only = parent && parent.only;
40237 this.beforeEachFns = [];
40238 this.afterEachFns = [];
40240 this.children = [];
40241 this.name = descName;
40242 this.parent = parent;
40243 this.id = angular.scenario.Describe.id++;
40246 * Calls all before functions.
40248 var beforeEachFns = this.beforeEachFns;
40249 this.setupBefore = function() {
40250 if (parent) parent.setupBefore.call(this);
40251 angular.forEach(beforeEachFns, function(fn) { fn.call(this); }, this);
40255 * Calls all after functions.
40257 var afterEachFns = this.afterEachFns;
40258 this.setupAfter = function() {
40259 angular.forEach(afterEachFns, function(fn) { fn.call(this); }, this);
40260 if (parent) parent.setupAfter.call(this);
40264 // Shared Unique ID generator for every describe block
40265 angular.scenario.Describe.id = 0;
40267 // Shared Unique ID generator for every it (spec)
40268 angular.scenario.Describe.specId = 0;
40271 * Defines a block to execute before each it or nested describe.
40273 * @param {function()} body Body of the block.
40275 angular.scenario.Describe.prototype.beforeEach = function(body) {
40276 this.beforeEachFns.push(body);
40280 * Defines a block to execute after each it or nested describe.
40282 * @param {function()} body Body of the block.
40284 angular.scenario.Describe.prototype.afterEach = function(body) {
40285 this.afterEachFns.push(body);
40289 * Creates a new describe block that's a child of this one.
40291 * @param {string} name Name of the block. Appended to the parent block's name.
40292 * @param {function()} body Body of the block.
40294 angular.scenario.Describe.prototype.describe = function(name, body) {
40295 var child = new angular.scenario.Describe(name, this);
40296 this.children.push(child);
40301 * Same as describe() but makes ddescribe blocks the only to run.
40303 * @param {string} name Name of the test.
40304 * @param {function()} body Body of the block.
40306 angular.scenario.Describe.prototype.ddescribe = function(name, body) {
40307 var child = new angular.scenario.Describe(name, this);
40309 this.children.push(child);
40314 * Use to disable a describe block.
40316 angular.scenario.Describe.prototype.xdescribe = angular.noop;
40321 * @param {string} name Name of the test.
40322 * @param {function()} body Body of the block.
40324 angular.scenario.Describe.prototype.it = function(name, body) {
40326 id: angular.scenario.Describe.specId++,
40330 before: this.setupBefore,
40332 after: this.setupAfter
40337 * Same as it() but makes iit tests the only test to run.
40339 * @param {string} name Name of the test.
40340 * @param {function()} body Body of the block.
40342 angular.scenario.Describe.prototype.iit = function(name, body) {
40343 this.it.apply(this, arguments);
40344 this.its[this.its.length - 1].only = true;
40348 * Use to disable a test block.
40350 angular.scenario.Describe.prototype.xit = angular.noop;
40353 * Gets an array of functions representing all the tests (recursively).
40354 * that can be executed with SpecRunner's.
40356 * @return {Array<Object>} Array of it blocks {
40357 * definition : Object // parent Describe
40365 angular.scenario.Describe.prototype.getSpecs = function() {
40366 var specs = arguments[0] || [];
40367 angular.forEach(this.children, function(child) {
40368 child.getSpecs(specs);
40370 angular.forEach(this.its, function(it) {
40374 angular.forEach(specs, function(it) {
40379 return (only.length && only) || specs;
40383 * A future action in a spec.
40385 * @param {string} name name of the future action
40386 * @param {function()} behavior future callback(error, result)
40387 * @param {function()} line Optional. function that returns the file/line number.
40389 angular.scenario.Future = function(name, behavior, line) {
40391 this.behavior = behavior;
40392 this.fulfilled = false;
40393 this.value = undefined;
40394 this.parser = angular.identity;
40395 this.line = line || function() { return ''; };
40399 * Executes the behavior of the closure.
40401 * @param {function()} doneFn Callback function(error, result)
40403 angular.scenario.Future.prototype.execute = function(doneFn) {
40405 this.behavior(function(error, result) {
40406 self.fulfilled = true;
40409 result = self.parser(result);
40414 self.value = error || result;
40415 doneFn(error, result);
40420 * Configures the future to convert its final with a function fn(value)
40422 * @param {function()} fn function(value) that returns the parsed value
40424 angular.scenario.Future.prototype.parsedWith = function(fn) {
40430 * Configures the future to parse its final value from JSON
40433 angular.scenario.Future.prototype.fromJson = function() {
40434 return this.parsedWith(angular.fromJson);
40438 * Configures the future to convert its final value from objects
40441 angular.scenario.Future.prototype.toJson = function() {
40442 return this.parsedWith(angular.toJson);
40446 * Maintains an object tree from the runner events.
40448 * @param {Object} runner The scenario Runner instance to connect to.
40450 * TODO(esprehn): Every output type creates one of these, but we probably
40451 * want one global shared instance. Need to handle events better too
40452 * so the HTML output doesn't need to do spec model.getSpec(spec.id)
40455 * TODO(vojta) refactor on, emit methods (from all objects) - use inheritance
40457 angular.scenario.ObjectModel = function(runner) {
40461 this.listeners = [];
40467 runner.on('SpecBegin', function(spec) {
40468 var block = self.value,
40471 angular.forEach(self.getDefinitionPath(spec), function(def) {
40472 if (!block.children[def.name]) {
40473 block.children[def.name] = {
40480 block = block.children[def.name];
40481 definitions.push(def.name);
40484 var it = self.specMap[spec.id] =
40485 block.specs[spec.name] =
40486 new angular.scenario.ObjectModel.Spec(spec.id, spec.name, definitions);
40488 // forward the event
40489 self.emit('SpecBegin', it);
40492 runner.on('SpecError', function(spec, error) {
40493 var it = self.getSpec(spec.id);
40494 it.status = 'error';
40497 // forward the event
40498 self.emit('SpecError', it, error);
40501 runner.on('SpecEnd', function(spec) {
40502 var it = self.getSpec(spec.id);
40505 // forward the event
40506 self.emit('SpecEnd', it);
40509 runner.on('StepBegin', function(spec, step) {
40510 var it = self.getSpec(spec.id);
40511 step = new angular.scenario.ObjectModel.Step(step.name);
40512 it.steps.push(step);
40514 // forward the event
40515 self.emit('StepBegin', it, step);
40518 runner.on('StepEnd', function(spec) {
40519 var it = self.getSpec(spec.id);
40520 var step = it.getLastStep();
40521 if (step.name !== step.name) {
40522 throw 'Events fired in the wrong order. Step names don\'t match.';
40526 // forward the event
40527 self.emit('StepEnd', it, step);
40530 runner.on('StepFailure', function(spec, step, error) {
40531 var it = self.getSpec(spec.id),
40532 modelStep = it.getLastStep();
40534 modelStep.setErrorStatus('failure', error, step.line());
40535 it.setStatusFromStep(modelStep);
40537 // forward the event
40538 self.emit('StepFailure', it, modelStep, error);
40541 runner.on('StepError', function(spec, step, error) {
40542 var it = self.getSpec(spec.id),
40543 modelStep = it.getLastStep();
40545 modelStep.setErrorStatus('error', error, step.line());
40546 it.setStatusFromStep(modelStep);
40548 // forward the event
40549 self.emit('StepError', it, modelStep, error);
40552 runner.on('RunnerBegin', function() {
40553 self.emit('RunnerBegin');
40555 runner.on('RunnerEnd', function() {
40556 self.emit('RunnerEnd');
40559 function complete(item) {
40560 item.endTime = Date.now();
40561 item.duration = item.endTime - item.startTime;
40562 item.status = item.status || 'success';
40567 * Adds a listener for an event.
40569 * @param {string} eventName Name of the event to add a handler for
40570 * @param {function()} listener Function that will be called when event is fired
40572 angular.scenario.ObjectModel.prototype.on = function(eventName, listener) {
40573 eventName = eventName.toLowerCase();
40574 this.listeners[eventName] = this.listeners[eventName] || [];
40575 this.listeners[eventName].push(listener);
40579 * Emits an event which notifies listeners and passes extra
40582 * @param {string} eventName Name of the event to fire.
40584 angular.scenario.ObjectModel.prototype.emit = function(eventName) {
40586 args = Array.prototype.slice.call(arguments, 1);
40588 eventName = eventName.toLowerCase();
40590 if (this.listeners[eventName]) {
40591 angular.forEach(this.listeners[eventName], function(listener) {
40592 listener.apply(self, args);
40598 * Computes the path of definition describe blocks that wrap around
40601 * @param spec Spec to compute the path for.
40602 * @return {Array<Describe>} The describe block path
40604 angular.scenario.ObjectModel.prototype.getDefinitionPath = function(spec) {
40606 var currentDefinition = spec.definition;
40607 while (currentDefinition && currentDefinition.name) {
40608 path.unshift(currentDefinition);
40609 currentDefinition = currentDefinition.parent;
40615 * Gets a spec by id.
40617 * @param {string} id The id of the spec to get the object for.
40618 * @return {Object} the Spec instance
40620 angular.scenario.ObjectModel.prototype.getSpec = function(id) {
40621 return this.specMap[id];
40625 * A single it block.
40627 * @param {string} id Id of the spec
40628 * @param {string} name Name of the spec
40629 * @param {Array<string>=} definitionNames List of all describe block names that wrap this spec
40631 angular.scenario.ObjectModel.Spec = function(id, name, definitionNames) {
40634 this.startTime = Date.now();
40636 this.fullDefinitionName = (definitionNames || []).join(' ');
40640 * Adds a new step to the Spec.
40642 * @param {string} name Name of the step (really name of the future)
40643 * @return {Object} the added step
40645 angular.scenario.ObjectModel.Spec.prototype.addStep = function(name) {
40646 var step = new angular.scenario.ObjectModel.Step(name);
40647 this.steps.push(step);
40652 * Gets the most recent step.
40654 * @return {Object} the step
40656 angular.scenario.ObjectModel.Spec.prototype.getLastStep = function() {
40657 return this.steps[this.steps.length - 1];
40661 * Set status of the Spec from given Step
40663 * @param {angular.scenario.ObjectModel.Step} step
40665 angular.scenario.ObjectModel.Spec.prototype.setStatusFromStep = function(step) {
40666 if (!this.status || step.status == 'error') {
40667 this.status = step.status;
40668 this.error = step.error;
40669 this.line = step.line;
40674 * A single step inside a Spec.
40676 * @param {string} name Name of the step
40678 angular.scenario.ObjectModel.Step = function(name) {
40680 this.startTime = Date.now();
40684 * Helper method for setting all error status related properties
40686 * @param {string} status
40687 * @param {string} error
40688 * @param {string} line
40690 angular.scenario.ObjectModel.Step.prototype.setErrorStatus = function(status, error, line) {
40691 this.status = status;
40692 this.error = error;
40697 * Runner for scenarios
40699 * Has to be initialized before any test is loaded,
40700 * because it publishes the API into window (global space).
40702 angular.scenario.Runner = function($window) {
40703 this.listeners = [];
40704 this.$window = $window;
40705 this.rootDescribe = new angular.scenario.Describe();
40706 this.currentDescribe = this.rootDescribe;
40711 describe: this.describe,
40712 ddescribe: this.ddescribe,
40713 xdescribe: angular.noop,
40714 beforeEach: this.beforeEach,
40715 afterEach: this.afterEach
40717 angular.forEach(this.api, angular.bind(this, function(fn, key) {
40718 this.$window[key] = angular.bind(this, fn);
40723 * Emits an event which notifies listeners and passes extra
40726 * @param {string} eventName Name of the event to fire.
40728 angular.scenario.Runner.prototype.emit = function(eventName) {
40730 var args = Array.prototype.slice.call(arguments, 1);
40731 eventName = eventName.toLowerCase();
40732 if (!this.listeners[eventName]) {
40735 angular.forEach(this.listeners[eventName], function(listener) {
40736 listener.apply(self, args);
40741 * Adds a listener for an event.
40743 * @param {string} eventName The name of the event to add a handler for
40744 * @param {string} listener The fn(...) that takes the extra arguments from emit()
40746 angular.scenario.Runner.prototype.on = function(eventName, listener) {
40747 eventName = eventName.toLowerCase();
40748 this.listeners[eventName] = this.listeners[eventName] || [];
40749 this.listeners[eventName].push(listener);
40753 * Defines a describe block of a spec.
40757 * @param {string} name Name of the block
40758 * @param {function()} body Body of the block
40760 angular.scenario.Runner.prototype.describe = function(name, body) {
40762 this.currentDescribe.describe(name, function() {
40763 var parentDescribe = self.currentDescribe;
40764 self.currentDescribe = this;
40768 self.currentDescribe = parentDescribe;
40774 * Same as describe, but makes ddescribe the only blocks to run.
40778 * @param {string} name Name of the block
40779 * @param {function()} body Body of the block
40781 angular.scenario.Runner.prototype.ddescribe = function(name, body) {
40783 this.currentDescribe.ddescribe(name, function() {
40784 var parentDescribe = self.currentDescribe;
40785 self.currentDescribe = this;
40789 self.currentDescribe = parentDescribe;
40795 * Defines a test in a describe block of a spec.
40799 * @param {string} name Name of the block
40800 * @param {function()} body Body of the block
40802 angular.scenario.Runner.prototype.it = function(name, body) {
40803 this.currentDescribe.it(name, body);
40807 * Same as it, but makes iit tests the only tests to run.
40811 * @param {string} name Name of the block
40812 * @param {function()} body Body of the block
40814 angular.scenario.Runner.prototype.iit = function(name, body) {
40815 this.currentDescribe.iit(name, body);
40819 * Defines a function to be called before each it block in the describe
40820 * (and before all nested describes).
40824 * @param {function()} Callback to execute
40826 angular.scenario.Runner.prototype.beforeEach = function(body) {
40827 this.currentDescribe.beforeEach(body);
40831 * Defines a function to be called after each it block in the describe
40832 * (and before all nested describes).
40836 * @param {function()} Callback to execute
40838 angular.scenario.Runner.prototype.afterEach = function(body) {
40839 this.currentDescribe.afterEach(body);
40843 * Creates a new spec runner.
40846 * @param {Object} scope parent scope
40848 angular.scenario.Runner.prototype.createSpecRunner_ = function(scope) {
40849 var child = scope.$new();
40850 var Cls = angular.scenario.SpecRunner;
40852 // Export all the methods to child scope manually as now we don't mess controllers with scopes
40853 // TODO(vojta): refactor scenario runner so that these objects are not tightly coupled as current
40854 for (var name in Cls.prototype) {
40855 child[name] = angular.bind(child, Cls.prototype[name]);
40863 * Runs all the loaded tests with the specified runner class on the
40864 * provided application.
40866 * @param {angular.scenario.Application} application App to remote control.
40868 angular.scenario.Runner.prototype.run = function(application) {
40870 var $root = angular.injector(['ng']).get('$rootScope');
40871 angular.extend($root, this);
40872 angular.forEach(angular.scenario.Runner.prototype, function(fn, name) {
40873 $root[name] = angular.bind(self, fn);
40875 $root.application = application;
40876 $root.emit('RunnerBegin');
40877 asyncForEach(this.rootDescribe.getSpecs(), function(spec, specDone) {
40879 var runner = self.createSpecRunner_($root);
40880 angular.forEach(angular.scenario.dsl, function(fn, key) {
40881 dslCache[key] = fn.call($root);
40883 angular.forEach(angular.scenario.dsl, function(fn, key) {
40884 self.$window[key] = function() {
40885 var line = callerFile(3);
40886 var scope = runner.$new();
40888 // Make the dsl accessible on the current chain
40890 angular.forEach(dslCache, function(fn, key) {
40891 scope.dsl[key] = function() {
40892 return dslCache[key].apply(scope, arguments);
40896 // Make these methods work on the current chain
40897 scope.addFuture = function() {
40898 Array.prototype.push.call(arguments, line);
40899 return angular.scenario.SpecRunner.
40900 prototype.addFuture.apply(scope, arguments);
40902 scope.addFutureAction = function() {
40903 Array.prototype.push.call(arguments, line);
40904 return angular.scenario.SpecRunner.
40905 prototype.addFutureAction.apply(scope, arguments);
40908 return scope.dsl[key].apply(scope, arguments);
40911 runner.run(spec, function() {
40913 specDone.apply(this, arguments);
40918 self.emit('RunnerError', error);
40920 self.emit('RunnerEnd');
40925 * This class is the "this" of the it/beforeEach/afterEach method.
40926 * Responsibilities:
40927 * - "this" for it/beforeEach/afterEach
40928 * - keep state for single it/beforeEach/afterEach execution
40929 * - keep track of all of the futures to execute
40930 * - run single spec (execute each future)
40932 angular.scenario.SpecRunner = function() {
40934 this.afterIndex = 0;
40938 * Executes a spec which is an it block with associated before/after functions
40939 * based on the describe nesting.
40941 * @param {Object} spec A spec object
40942 * @param {function()} specDone function that is called when the spec finishes,
40943 * of the form `Function(error, index)`
40945 angular.scenario.SpecRunner.prototype.run = function(spec, specDone) {
40949 this.emit('SpecBegin', spec);
40952 spec.before.call(this);
40953 spec.body.call(this);
40954 this.afterIndex = this.futures.length;
40955 spec.after.call(this);
40957 this.emit('SpecError', spec, e);
40958 this.emit('SpecEnd', spec);
40963 var handleError = function(error, done) {
40968 done(null, self.afterIndex);
40973 function(future, futureDone) {
40974 self.step = future;
40975 self.emit('StepBegin', spec, future);
40977 future.execute(function(error) {
40979 self.emit('StepFailure', spec, future, error);
40980 self.emit('StepEnd', spec, future);
40981 return handleError(error, futureDone);
40983 self.emit('StepEnd', spec, future);
40984 self.$window.setTimeout(function() { futureDone(); }, 0);
40987 self.emit('StepError', spec, future, e);
40988 self.emit('StepEnd', spec, future);
40989 handleError(e, futureDone);
40994 self.emit('SpecError', spec, e);
40996 self.emit('SpecEnd', spec);
40997 // Call done in a timeout so exceptions don't recursively
40998 // call this function
40999 self.$window.setTimeout(function() { specDone(); }, 0);
41005 * Adds a new future action.
41007 * Note: Do not pass line manually. It happens automatically.
41009 * @param {string} name Name of the future
41010 * @param {function()} behavior Behavior of the future
41011 * @param {function()} line fn() that returns file/line number
41013 angular.scenario.SpecRunner.prototype.addFuture = function(name, behavior, line) {
41014 var future = new angular.scenario.Future(name, angular.bind(this, behavior), line);
41015 this.futures.push(future);
41020 * Adds a new future action to be executed on the application window.
41022 * Note: Do not pass line manually. It happens automatically.
41024 * @param {string} name Name of the future
41025 * @param {function()} behavior Behavior of the future
41026 * @param {function()} line fn() that returns file/line number
41028 angular.scenario.SpecRunner.prototype.addFutureAction = function(name, behavior, line) {
41030 var NG = /\[ng\\\:/;
41031 return this.addFuture(name, function(done) {
41032 this.application.executeAction(function($window, $document) {
41034 //TODO(esprehn): Refactor this so it doesn't need to be in here.
41035 $document.elements = function(selector) {
41036 var args = Array.prototype.slice.call(arguments, 1);
41037 selector = (self.selector || '') + ' ' + (selector || '');
41038 selector = _jQuery.trim(selector) || '*';
41039 angular.forEach(args, function(value, index) {
41040 selector = selector.replace('$' + (index + 1), value);
41042 var result = $document.find(selector);
41043 if (selector.match(NG)) {
41044 angular.forEach(['[ng-','[data-ng-','[x-ng-'], function(value, index) {
41045 result = result.add(selector.replace(NG, value), $document);
41048 if (!result.length) {
41051 message: 'Selector ' + selector + ' did not match any elements.'
41059 behavior.call(self, $window, $document, done);
41061 if (e.type && e.type === 'selector') {
41072 * Shared DSL statements that are useful to all scenarios.
41077 * pause() pauses until you call resume() in the console
41079 angular.scenario.dsl('pause', function() {
41080 return function() {
41081 return this.addFuture('pausing for you to resume', function(done) {
41082 this.emit('InteractivePause', this.spec, this.step);
41083 this.$window.resume = function() { done(); };
41090 * sleep(seconds) pauses the test for specified number of seconds
41092 angular.scenario.dsl('sleep', function() {
41093 return function(time) {
41094 return this.addFuture('sleep for ' + time + ' seconds', function(done) {
41095 this.$window.setTimeout(function() { done(null, time * 1000); }, time * 1000);
41102 * browser().navigateTo(url) Loads the url into the frame
41103 * browser().navigateTo(url, fn) where fn(url) is called and returns the URL to navigate to
41104 * browser().reload() refresh the page (reload the same URL)
41105 * browser().window.href() window.location.href
41106 * browser().window.path() window.location.pathname
41107 * browser().window.search() window.location.search
41108 * browser().window.hash() window.location.hash without # prefix
41109 * browser().location().url() see ng.$location#url
41110 * browser().location().path() see ng.$location#path
41111 * browser().location().search() see ng.$location#search
41112 * browser().location().hash() see ng.$location#hash
41114 angular.scenario.dsl('browser', function() {
41117 chain.navigateTo = function(url, delegate) {
41118 var application = this.application;
41119 return this.addFuture("browser navigate to '" + url + "'", function(done) {
41121 url = delegate.call(this, url);
41123 application.navigateTo(url, function() {
41129 chain.reload = function() {
41130 var application = this.application;
41131 return this.addFutureAction('browser reload', function($window, $document, done) {
41132 var href = $window.location.href;
41133 application.navigateTo(href, function() {
41139 chain.window = function() {
41142 api.href = function() {
41143 return this.addFutureAction('window.location.href', function($window, $document, done) {
41144 done(null, $window.location.href);
41148 api.path = function() {
41149 return this.addFutureAction('window.location.path', function($window, $document, done) {
41150 done(null, $window.location.pathname);
41154 api.search = function() {
41155 return this.addFutureAction('window.location.search', function($window, $document, done) {
41156 done(null, $window.location.search);
41160 api.hash = function() {
41161 return this.addFutureAction('window.location.hash', function($window, $document, done) {
41162 done(null, $window.location.hash.replace('#', ''));
41169 chain.location = function() {
41172 api.url = function() {
41173 return this.addFutureAction('$location.url()', function($window, $document, done) {
41174 done(null, $document.injector().get('$location').url());
41178 api.path = function() {
41179 return this.addFutureAction('$location.path()', function($window, $document, done) {
41180 done(null, $document.injector().get('$location').path());
41184 api.search = function() {
41185 return this.addFutureAction('$location.search()', function($window, $document, done) {
41186 done(null, $document.injector().get('$location').search());
41190 api.hash = function() {
41191 return this.addFutureAction('$location.hash()', function($window, $document, done) {
41192 done(null, $document.injector().get('$location').hash());
41199 return function() {
41206 * expect(future).{matcher} where matcher is one of the matchers defined
41207 * with angular.scenario.matcher
41209 * ex. expect(binding("name")).toEqual("Elliott")
41211 angular.scenario.dsl('expect', function() {
41212 var chain = angular.extend({}, angular.scenario.matcher);
41214 chain.not = function() {
41215 this.inverse = true;
41219 return function(future) {
41220 this.future = future;
41227 * using(selector, label) scopes the next DSL element selection
41230 * using('#foo', "'Foo' text field").input('bar')
41232 angular.scenario.dsl('using', function() {
41233 return function(selector, label) {
41234 this.selector = _jQuery.trim((this.selector || '') + ' ' + selector);
41235 if (angular.isString(label) && label.length) {
41236 this.label = label + ' ( ' + this.selector + ' )';
41238 this.label = this.selector;
41246 * binding(name) returns the value of the first matching binding
41248 angular.scenario.dsl('binding', function() {
41249 return function(name) {
41250 return this.addFutureAction("select binding '" + name + "'",
41251 function($window, $document, done) {
41252 var values = $document.elements().bindings($window.angular.element, name);
41253 if (!values.length) {
41254 return done("Binding selector '" + name + "' did not match.");
41256 done(null, values[0]);
41263 * input(name).enter(value) enters value in input with specified name
41264 * input(name).check() checks checkbox
41265 * input(name).select(value) selects the radio button with specified name/value
41266 * input(name).val() returns the value of the input.
41268 angular.scenario.dsl('input', function() {
41270 var supportInputEvent = 'oninput' in document.createElement('div') && !(msie && msie <= 11);
41272 chain.enter = function(value, event) {
41273 return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",
41274 function($window, $document, done) {
41275 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
41277 input.trigger(event || (supportInputEvent ? 'input' : 'change'));
41282 chain.check = function() {
41283 return this.addFutureAction("checkbox '" + this.name + "' toggle",
41284 function($window, $document, done) {
41285 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':checkbox');
41286 input.trigger('click');
41291 chain.select = function(value) {
41292 return this.addFutureAction("radio button '" + this.name + "' toggle '" + value + "'",
41293 function($window, $document, done) {
41294 var input = $document.
41295 elements('[ng\\:model="$1"][value="$2"]', this.name, value).filter(':radio');
41296 input.trigger('click');
41301 chain.val = function() {
41302 return this.addFutureAction("return input val", function($window, $document, done) {
41303 var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
41304 done(null,input.val());
41308 return function(name) {
41317 * repeater('#products table', 'Product List').count() number of rows
41318 * repeater('#products table', 'Product List').row(1) all bindings in row as an array
41319 * repeater('#products table', 'Product List').column('product.name') all values across all rows
41322 angular.scenario.dsl('repeater', function() {
41325 chain.count = function() {
41326 return this.addFutureAction("repeater '" + this.label + "' count",
41327 function($window, $document, done) {
41329 done(null, $document.elements().length);
41336 chain.column = function(binding) {
41337 return this.addFutureAction("repeater '" + this.label + "' column '" + binding + "'",
41338 function($window, $document, done) {
41339 done(null, $document.elements().bindings($window.angular.element, binding));
41343 chain.row = function(index) {
41344 return this.addFutureAction("repeater '" + this.label + "' row '" + index + "'",
41345 function($window, $document, done) {
41346 var matches = $document.elements().slice(index, index + 1);
41347 if (!matches.length) {
41348 return done('row ' + index + ' out of bounds');
41350 done(null, matches.bindings($window.angular.element));
41354 return function(selector, label) {
41355 this.dsl.using(selector, label);
41362 * select(name).option('value') select one option
41363 * select(name).options('value1', 'value2', ...) select options from a multi select
41365 angular.scenario.dsl('select', function() {
41368 chain.option = function(value) {
41369 return this.addFutureAction("select '" + this.name + "' option '" + value + "'",
41370 function($window, $document, done) {
41371 var select = $document.elements('select[ng\\:model="$1"]', this.name);
41372 var option = select.find('option[value="' + value + '"]');
41373 if (option.length) {
41376 option = select.find('option').filter(function() {
41377 return _jQuery(this).text() === value;
41379 if (!option.length) {
41380 option = select.find('option:contains("' + value + '")');
41382 if (option.length) {
41383 select.val(option.val());
41385 return done("option '" + value + "' not found");
41388 select.trigger('change');
41393 chain.options = function() {
41394 var values = arguments;
41395 return this.addFutureAction("select '" + this.name + "' options '" + values + "'",
41396 function($window, $document, done) {
41397 var select = $document.elements('select[multiple][ng\\:model="$1"]', this.name);
41398 select.val(values);
41399 select.trigger('change');
41404 return function(name) {
41412 * element(selector, label).count() get the number of elements that match selector
41413 * element(selector, label).click() clicks an element
41414 * element(selector, label).mouseover() mouseover an element
41415 * element(selector, label).mousedown() mousedown an element
41416 * element(selector, label).mouseup() mouseup an element
41417 * element(selector, label).query(fn) executes fn(selectedElements, done)
41418 * element(selector, label).{method}() gets the value (as defined by jQuery, ex. val)
41419 * element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val)
41420 * element(selector, label).{method}(key) gets the value (as defined by jQuery, ex. attr)
41421 * element(selector, label).{method}(key, value) sets the value (as defined by jQuery, ex. attr)
41423 angular.scenario.dsl('element', function() {
41424 var KEY_VALUE_METHODS = ['attr', 'css', 'prop'];
41425 var VALUE_METHODS = [
41426 'val', 'text', 'html', 'height', 'innerHeight', 'outerHeight', 'width',
41427 'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset'
41431 chain.count = function() {
41432 return this.addFutureAction("element '" + this.label + "' count",
41433 function($window, $document, done) {
41435 done(null, $document.elements().length);
41442 chain.click = function() {
41443 return this.addFutureAction("element '" + this.label + "' click",
41444 function($window, $document, done) {
41445 var elements = $document.elements();
41446 var href = elements.attr('href');
41447 var eventProcessDefault = elements.trigger('click')[0];
41449 if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) {
41450 this.application.navigateTo(href, function() {
41459 chain.dblclick = function() {
41460 return this.addFutureAction("element '" + this.label + "' dblclick",
41461 function($window, $document, done) {
41462 var elements = $document.elements();
41463 var href = elements.attr('href');
41464 var eventProcessDefault = elements.trigger('dblclick')[0];
41466 if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) {
41467 this.application.navigateTo(href, function() {
41476 chain.mouseover = function() {
41477 return this.addFutureAction("element '" + this.label + "' mouseover",
41478 function($window, $document, done) {
41479 var elements = $document.elements();
41480 elements.trigger('mouseover');
41485 chain.mousedown = function() {
41486 return this.addFutureAction("element '" + this.label + "' mousedown",
41487 function($window, $document, done) {
41488 var elements = $document.elements();
41489 elements.trigger('mousedown');
41494 chain.mouseup = function() {
41495 return this.addFutureAction("element '" + this.label + "' mouseup",
41496 function($window, $document, done) {
41497 var elements = $document.elements();
41498 elements.trigger('mouseup');
41503 chain.query = function(fn) {
41504 return this.addFutureAction('element ' + this.label + ' custom query',
41505 function($window, $document, done) {
41506 fn.call(this, $document.elements(), done);
41510 angular.forEach(KEY_VALUE_METHODS, function(methodName) {
41511 chain[methodName] = function(name, value) {
41512 var args = arguments,
41513 futureName = (args.length == 1)
41514 ? "element '" + this.label + "' get " + methodName + " '" + name + "'"
41515 : "element '" + this.label + "' set " + methodName + " '" + name + "' to " + "'" +
41518 return this.addFutureAction(futureName, function($window, $document, done) {
41519 var element = $document.elements();
41520 done(null, element[methodName].apply(element, args));
41525 angular.forEach(VALUE_METHODS, function(methodName) {
41526 chain[methodName] = function(value) {
41527 var args = arguments,
41528 futureName = (args.length === 0)
41529 ? "element '" + this.label + "' " + methodName
41530 : "element '" + this.label + "' set " + methodName + " to '" + value + "'";
41532 return this.addFutureAction(futureName, function($window, $document, done) {
41533 var element = $document.elements();
41534 done(null, element[methodName].apply(element, args));
41539 return function(selector, label) {
41540 this.dsl.using(selector, label);
41546 * Matchers for implementing specs. Follows the Jasmine spec conventions.
41549 angular.scenario.matcher('toEqual', function(expected) {
41550 return angular.equals(this.actual, expected);
41553 angular.scenario.matcher('toBe', function(expected) {
41554 return this.actual === expected;
41557 angular.scenario.matcher('toBeDefined', function() {
41558 return angular.isDefined(this.actual);
41561 angular.scenario.matcher('toBeTruthy', function() {
41562 return this.actual;
41565 angular.scenario.matcher('toBeFalsy', function() {
41566 return !this.actual;
41569 angular.scenario.matcher('toMatch', function(expected) {
41570 return new RegExp(expected).test(this.actual);
41573 angular.scenario.matcher('toBeNull', function() {
41574 return this.actual === null;
41577 angular.scenario.matcher('toContain', function(expected) {
41578 return includes(this.actual, expected);
41581 angular.scenario.matcher('toBeLessThan', function(expected) {
41582 return this.actual < expected;
41585 angular.scenario.matcher('toBeGreaterThan', function(expected) {
41586 return this.actual > expected;
41590 * User Interface for the Scenario Runner.
41592 * TODO(esprehn): This should be refactored now that ObjectModel exists
41593 * to use angular bindings for the UI.
41595 angular.scenario.output('html', function(context, runner, model) {
41596 var specUiMap = {},
41597 lastStepUiMap = {};
41600 '<div id="header">' +
41601 ' <h1><span class="angular">AngularJS</span>: Scenario Test Runner</h1>' +
41602 ' <ul id="status-legend" class="status-display">' +
41603 ' <li class="status-error">0 Errors</li>' +
41604 ' <li class="status-failure">0 Failures</li>' +
41605 ' <li class="status-success">0 Passed</li>' +
41608 '<div id="specs">' +
41609 ' <div class="test-children"></div>' +
41613 runner.on('InteractivePause', function(spec) {
41614 var ui = lastStepUiMap[spec.id];
41615 ui.find('.test-title').
41616 html('paused... <a href="javascript:resume()">resume</a> when ready.');
41619 runner.on('SpecBegin', function(spec) {
41620 var ui = findContext(spec);
41621 ui.find('> .tests').append(
41622 '<li class="status-pending test-it"></li>'
41624 ui = ui.find('> .tests li:last');
41626 '<div class="test-info">' +
41627 ' <p class="test-title">' +
41628 ' <span class="timer-result"></span>' +
41629 ' <span class="test-name"></span>' +
41632 '<div class="scrollpane">' +
41633 ' <ol class="test-actions"></ol>' +
41636 ui.find('> .test-info .test-name').text(spec.name);
41637 ui.find('> .test-info').click(function() {
41638 var scrollpane = ui.find('> .scrollpane');
41639 var actions = scrollpane.find('> .test-actions');
41640 var name = context.find('> .test-info .test-name');
41641 if (actions.find(':visible').length) {
41643 name.removeClass('open').addClass('closed');
41646 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
41647 name.removeClass('closed').addClass('open');
41651 specUiMap[spec.id] = ui;
41654 runner.on('SpecError', function(spec, error) {
41655 var ui = specUiMap[spec.id];
41656 ui.append('<pre></pre>');
41657 ui.find('> pre').text(formatException(error));
41660 runner.on('SpecEnd', function(spec) {
41661 var ui = specUiMap[spec.id];
41662 spec = model.getSpec(spec.id);
41663 ui.removeClass('status-pending');
41664 ui.addClass('status-' + spec.status);
41665 ui.find("> .test-info .timer-result").text(spec.duration + "ms");
41666 if (spec.status === 'success') {
41667 ui.find('> .test-info .test-name').addClass('closed');
41668 ui.find('> .scrollpane .test-actions').hide();
41670 updateTotals(spec.status);
41673 runner.on('StepBegin', function(spec, step) {
41674 var ui = specUiMap[spec.id];
41675 spec = model.getSpec(spec.id);
41676 step = spec.getLastStep();
41677 ui.find('> .scrollpane .test-actions').append('<li class="status-pending"></li>');
41678 var stepUi = lastStepUiMap[spec.id] = ui.find('> .scrollpane .test-actions li:last');
41680 '<div class="timer-result"></div>' +
41681 '<div class="test-title"></div>'
41683 stepUi.find('> .test-title').text(step.name);
41684 var scrollpane = stepUi.parents('.scrollpane');
41685 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
41688 runner.on('StepFailure', function(spec, step, error) {
41689 var ui = lastStepUiMap[spec.id];
41690 addError(ui, step.line, error);
41693 runner.on('StepError', function(spec, step, error) {
41694 var ui = lastStepUiMap[spec.id];
41695 addError(ui, step.line, error);
41698 runner.on('StepEnd', function(spec, step) {
41699 var stepUi = lastStepUiMap[spec.id];
41700 spec = model.getSpec(spec.id);
41701 step = spec.getLastStep();
41702 stepUi.find('.timer-result').text(step.duration + 'ms');
41703 stepUi.removeClass('status-pending');
41704 stepUi.addClass('status-' + step.status);
41705 var scrollpane = specUiMap[spec.id].find('> .scrollpane');
41706 scrollpane.attr('scrollTop', scrollpane.attr('scrollHeight'));
41710 * Finds the context of a spec block defined by the passed definition.
41712 * @param {Object} The definition created by the Describe object.
41714 function findContext(spec) {
41715 var currentContext = context.find('#specs');
41716 angular.forEach(model.getDefinitionPath(spec), function(defn) {
41717 var id = 'describe-' + defn.id;
41718 if (!context.find('#' + id).length) {
41719 currentContext.find('> .test-children').append(
41720 '<div class="test-describe" id="' + id + '">' +
41722 ' <div class="test-children"></div>' +
41723 ' <ul class="tests"></ul>' +
41726 context.find('#' + id).find('> h2').text('describe: ' + defn.name);
41728 currentContext = context.find('#' + id);
41730 return context.find('#describe-' + spec.definition.id);
41734 * Updates the test counter for the status.
41736 * @param {string} the status.
41738 function updateTotals(status) {
41739 var legend = context.find('#status-legend .status-' + status);
41740 var parts = legend.text().split(' ');
41741 var value = (parts[0] * 1) + 1;
41742 legend.text(value + ' ' + parts[1]);
41746 * Add an error to a step.
41748 * @param {Object} The JQuery wrapped context
41749 * @param {function()} fn() that should return the file/line number of the error
41750 * @param {Object} the error.
41752 function addError(context, line, error) {
41753 context.find('.test-title').append('<pre></pre>');
41754 var message = _jQuery.trim(line() + '\n\n' + formatException(error));
41755 context.find('.test-title pre:last').text(message);
41760 * Generates JSON output into a context.
41762 angular.scenario.output('json', function(context, runner, model) {
41763 model.on('RunnerEnd', function() {
41764 context.text(angular.toJson(model.value));
41769 * Generates XML output into a context.
41771 angular.scenario.output('xml', function(context, runner, model) {
41772 var $ = function(args) {return new context.init(args);};
41773 model.on('RunnerEnd', function() {
41774 var scenario = $('<scenario></scenario>');
41775 context.append(scenario);
41776 serializeXml(scenario, model.value);
41780 * Convert the tree into XML.
41782 * @param {Object} context jQuery context to add the XML to.
41783 * @param {Object} tree node to serialize
41785 function serializeXml(context, tree) {
41786 angular.forEach(tree.children, function(child) {
41787 var describeContext = $('<describe></describe>');
41788 describeContext.attr('id', child.id);
41789 describeContext.attr('name', child.name);
41790 context.append(describeContext);
41791 serializeXml(describeContext, child);
41793 var its = $('<its></its>');
41794 context.append(its);
41795 angular.forEach(tree.specs, function(spec) {
41796 var it = $('<it></it>');
41797 it.attr('id', spec.id);
41798 it.attr('name', spec.name);
41799 it.attr('duration', spec.duration);
41800 it.attr('status', spec.status);
41802 angular.forEach(spec.steps, function(step) {
41803 var stepContext = $('<step></step>');
41804 stepContext.attr('name', step.name);
41805 stepContext.attr('duration', step.duration);
41806 stepContext.attr('status', step.status);
41807 it.append(stepContext);
41809 var error = $('<error></error>');
41810 stepContext.append(error);
41811 error.text(formatException(step.error));
41819 * Creates a global value $result with the result of the runner.
41821 angular.scenario.output('object', function(context, runner, model) {
41822 runner.$window.$result = model.value;
41826 publishExternalAPI(angular);
41828 var $runner = new angular.scenario.Runner(window),
41829 scripts = document.getElementsByTagName('script'),
41830 script = scripts[scripts.length - 1],
41833 angular.forEach(script.attributes, function(attr) {
41834 var match = attr.name.match(/ng[:\-](.*)/);
41836 config[match[1]] = attr.value || true;
41840 if (config.autotest) {
41841 JQLite(document).ready(function() {
41842 angular.scenario.setUpAndRun(config);
41845 })(window, document);
41848 !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>');
41849 !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>');